From e78056720bd85de0fb6d6d656b94d02b1d572fd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?An=C4=B1lcan=20=C3=87ak=C4=B1r?= Date: Fri, 26 Jun 2026 01:55:10 +0300 Subject: [PATCH 1/6] feat: design-first magic_example (full-set app + DESIGN.md + /preview catalog + agent infra) Ready-to-start reference app for the fluttersdk ecosystem: - Full set installed: magic, magic_starter, dusk, telescope, magic_deeplink, magic_notifications, magic_social_auth, on fluttersdk_artisan; a Laravel api/v1 backend (magic-starter-laravel) under backend/. - DESIGN.md is the single source of truth; design:sync generates the wind theme (lib/config/wind_theme.g.dart), consumed in main.dart. - Dev-only /preview catalog (magic_devtools) listing foundations, the component library, and feature-screen previews driven by a backend-free mock harness; stripped from release builds. - Design-first agent infra: CLAUDE.md + AGENTS.md + .claude/rules + 3 skills + a component-visual-reviewer agent + docs/design-culture + component-registry. Verified locally end to end: flutter analyze 0, the app boots and renders (login/dashboard/profile + the catalog) in light and dark, the real backend api/v1/auth/login returns a Sanctum token, and release builds tree-shake the catalog. --- .claude/agents/component-visual-reviewer.md | 156 + .claude/rules/design.md | 109 + .claude/skills/design-first-workflow/SKILL.md | 207 + .claude/skills/frontend-design/SKILL.md | 357 + .claude/skills/make-component/SKILL.md | 198 + .env.example | 6 + .metadata | 45 + AGENTS.md | 92 + CLAUDE.md | 66 + DESIGN.md | 225 + analysis_options.yaml | 28 + android/.gitignore | 14 + android/app/build.gradle.kts | 45 + android/app/src/debug/AndroidManifest.xml | 7 + android/app/src/main/AndroidManifest.xml | 45 + .../fluttersdk/magic_example/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 | 6 + .../gradle/wrapper/gradle-wrapper.properties | 5 + android/settings.gradle.kts | 26 + assets/lang/en.json | 290 + backend/.editorconfig | 18 + backend/.env.example | 65 + backend/.gitattributes | 11 + backend/.gitignore | 27 + backend/.npmrc | 2 + backend/README.md | 58 + .../Actions/MagicStarter/AddTeamMember.php | 28 + .../app/Actions/MagicStarter/CreateTeam.php | 27 + .../app/Actions/MagicStarter/CreateUser.php | 25 + .../app/Actions/MagicStarter/DeleteTeam.php | 24 + .../app/Actions/MagicStarter/DeleteUser.php | 24 + .../Actions/MagicStarter/InviteTeamMember.php | 29 + .../Actions/MagicStarter/RemoveTeamMember.php | 27 + .../app/Actions/MagicStarter/UpdateTeam.php | 27 + .../MagicStarter/UpdateTeamMemberRole.php | 28 + .../MagicStarter/UpdateUserPassword.php | 25 + .../MagicStarter/UpdateUserProfile.php | 25 + backend/app/Http/Controllers/Controller.php | 8 + backend/app/Models/Team.php | 23 + backend/app/Models/TeamInvitation.php | 11 + backend/app/Models/TeamUser.php | 10 + backend/app/Models/User.php | 71 + backend/app/Policies/TeamPolicy.php | 62 + backend/app/Providers/AppServiceProvider.php | 24 + backend/artisan | 18 + backend/bootstrap/app.php | 21 + backend/bootstrap/cache/.gitignore | 2 + backend/bootstrap/providers.php | 7 + backend/composer.json | 95 + backend/composer.lock | 9104 +++++++++++++++++ backend/config/app.php | 126 + backend/config/auth.php | 117 + backend/config/cache.php | 136 + backend/config/database.php | 184 + backend/config/filesystems.php | 80 + backend/config/logging.php | 132 + backend/config/magic-starter.php | 298 + backend/config/mail.php | 118 + backend/config/queue.php | 129 + backend/config/services.php | 38 + backend/config/session.php | 233 + backend/database/.gitignore | 1 + backend/database/factories/UserFactory.php | 45 + .../0001_01_01_000000_create_users_table.php | 49 + .../0001_01_01_000001_create_cache_table.php | 35 + .../0001_01_01_000002_create_jobs_table.php | 59 + .../2026_06_24_100010_create_teams_table.php | 28 + ...26_06_24_100020_create_team_user_table.php | 29 + ...4_100030_create_team_invitations_table.php | 30 + ...40_create_personal_access_tokens_table.php | 32 + ...6_24_100050_create_notifications_table.php | 40 + ...060_create_notification_settings_table.php | 41 + ...70_create_newsletter_subscribers_table.php | 27 + ...010_add_current_team_id_to_users_table.php | 28 + ...e_info_to_personal_access_tokens_table.php | 35 + ...d_expires_at_to_team_invitations_table.php | 26 + ..._guest_and_phone_fields_to_users_table.php | 66 + ...add_localization_fields_to_users_table.php | 35 + ...0060_add_profile_fields_to_users_table.php | 26 + ..._add_profile_photo_path_to_teams_table.php | 26 + ..._add_profile_photo_path_to_users_table.php | 26 + ..._24_200090_add_timezone_to_users_table.php | 26 + ..._add_two_factor_columns_to_users_table.php | 67 + ..._drop_language_column_from_users_table.php | 26 + backend/database/seeders/DatabaseSeeder.php | 25 + .../lang/vendor/magic-starter/en/teams.php | 6 + .../lang/vendor/magic-starter/tr/teams.php | 6 + backend/package.json | 16 + backend/phpunit.xml | 36 + backend/public/.htaccess | 25 + backend/public/favicon.ico | 0 backend/public/index.php | 20 + backend/public/robots.txt | 2 + backend/resources/css/app.css | 9 + backend/resources/js/app.js | 1 + backend/resources/views/welcome.blade.php | 223 + backend/routes/console.php | 8 + backend/routes/web.php | 7 + backend/storage/app/.gitignore | 4 + backend/storage/app/private/.gitignore | 2 + backend/storage/app/public/.gitignore | 2 + backend/storage/framework/.gitignore | 9 + backend/storage/framework/cache/.gitignore | 3 + .../storage/framework/cache/data/.gitignore | 2 + backend/storage/framework/sessions/.gitignore | 2 + backend/storage/framework/testing/.gitignore | 2 + backend/storage/framework/views/.gitignore | 2 + backend/storage/logs/.gitignore | 2 + backend/tests/Feature/ExampleTest.php | 19 + backend/tests/TestCase.php | 10 + backend/tests/Unit/ExampleTest.php | 16 + backend/vite.config.js | 24 + bin/dispatcher.dart | 38 + bin/fsa | 106 + docs/component-registry.md | 510 + docs/design-culture/accessibility-wcag.md | 201 + docs/design-culture/apple-hig.md | 134 + docs/design-culture/material-design-3.md | 216 + docs/design-culture/motion-interaction.md | 214 + docs/design-culture/refactoring-ui.md | 185 + docs/design-culture/wind-responsive.md | 263 + ios/.gitignore | 34 + ios/Flutter/AppFrameworkInfo.plist | 24 + ios/Flutter/Debug.xcconfig | 1 + ios/Flutter/Release.xcconfig | 1 + ios/Runner.xcodeproj/project.pbxproj | 647 ++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 119 + .../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/_plugins.g.dart | 27 + lib/app/commands/_index.g.dart | 7 + lib/app/kernel.dart | 55 + lib/app/middleware/ensure_authenticated.dart | 23 + .../middleware/redirect_if_authenticated.dart | 23 + lib/app/models/team.dart | 177 + lib/app/models/user.dart | 189 + lib/app/providers/app_service_provider.dart | 76 + lib/app/providers/route_service_provider.dart | 40 + lib/config/app.dart | 33 + lib/config/auth.dart | 56 + lib/config/broadcasting.dart | 26 + lib/config/cache.dart | 9 + lib/config/database.dart | 12 + lib/config/deeplink.dart | 17 + lib/config/logging.dart | 16 + lib/config/magic_starter.dart | 36 + lib/config/network.dart | 22 + lib/config/routing.dart | 11 + lib/config/view.dart | 21 + lib/config/wind_theme.g.dart | 52 + lib/main.dart | 61 + lib/preview/_previews.g.dart | 59 + lib/preview/components.preview.dart | 190 + lib/preview/dashboard_screen.preview.dart | 22 + lib/preview/foundations.preview.dart | 137 + lib/preview/login_screen.preview.dart | 21 + lib/preview/preview_mock_harness.dart | 292 + lib/preview/profile_screen.preview.dart | 24 + lib/preview/register_screen.preview.dart | 23 + lib/preview/screen_preview_scaffold.dart | 78 + lib/preview/settings_screen.preview.dart | 24 + lib/preview/teams_screen.preview.dart | 24 + lib/resources/views/dashboard_view.dart | 158 + lib/resources/views/welcome_view.dart | 167 + lib/routes/app.dart | 25 + linux/.gitignore | 1 + linux/CMakeLists.txt | 128 + linux/flutter/CMakeLists.txt | 88 + linux/flutter/generated_plugin_registrant.cc | 35 + linux/flutter/generated_plugin_registrant.h | 15 + linux/flutter/generated_plugins.cmake | 30 + 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 | 32 + macos/Runner.xcodeproj/project.pbxproj | 729 ++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/swiftpm/Package.resolved | 77 + .../xcshareddata/xcschemes/Runner.xcscheme | 117 + .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/swiftpm/Package.resolved | 77 + 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 | 16 + macos/Runner/Info.plist | 32 + macos/Runner/MainFlutterWindow.swift | 15 + macos/Runner/Release.entitlements | 12 + macos/RunnerTests/RunnerTests.swift | 12 + pubspec.lock | 1276 +++ pubspec.yaml | 135 + test/widget_test.dart | 10 + 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 + web/sqlite3.wasm | Bin 0 -> 744124 bytes windows/.gitignore | 17 + windows/CMakeLists.txt | 108 + windows/flutter/CMakeLists.txt | 109 + .../flutter/generated_plugin_registrant.cc | 32 + windows/flutter/generated_plugin_registrant.h | 15 + windows/flutter/generated_plugins.cmake | 31 + 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 | 69 + windows/runner/utils.h | 19 + windows/runner/win32_window.cpp | 288 + windows/runner/win32_window.h | 102 + 277 files changed, 24850 insertions(+) create mode 100644 .claude/agents/component-visual-reviewer.md create mode 100644 .claude/rules/design.md create mode 100644 .claude/skills/design-first-workflow/SKILL.md create mode 100644 .claude/skills/frontend-design/SKILL.md create mode 100644 .claude/skills/make-component/SKILL.md create mode 100644 .env.example create mode 100644 .metadata create mode 100644 AGENTS.md create mode 100644 CLAUDE.md create mode 100644 DESIGN.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/fluttersdk/magic_example/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/lang/en.json create mode 100644 backend/.editorconfig create mode 100644 backend/.env.example create mode 100644 backend/.gitattributes create mode 100644 backend/.gitignore create mode 100644 backend/.npmrc create mode 100644 backend/README.md create mode 100644 backend/app/Actions/MagicStarter/AddTeamMember.php create mode 100644 backend/app/Actions/MagicStarter/CreateTeam.php create mode 100644 backend/app/Actions/MagicStarter/CreateUser.php create mode 100644 backend/app/Actions/MagicStarter/DeleteTeam.php create mode 100644 backend/app/Actions/MagicStarter/DeleteUser.php create mode 100644 backend/app/Actions/MagicStarter/InviteTeamMember.php create mode 100644 backend/app/Actions/MagicStarter/RemoveTeamMember.php create mode 100644 backend/app/Actions/MagicStarter/UpdateTeam.php create mode 100644 backend/app/Actions/MagicStarter/UpdateTeamMemberRole.php create mode 100644 backend/app/Actions/MagicStarter/UpdateUserPassword.php create mode 100644 backend/app/Actions/MagicStarter/UpdateUserProfile.php create mode 100644 backend/app/Http/Controllers/Controller.php create mode 100644 backend/app/Models/Team.php create mode 100644 backend/app/Models/TeamInvitation.php create mode 100644 backend/app/Models/TeamUser.php create mode 100644 backend/app/Models/User.php create mode 100644 backend/app/Policies/TeamPolicy.php create mode 100644 backend/app/Providers/AppServiceProvider.php create mode 100755 backend/artisan create mode 100644 backend/bootstrap/app.php create mode 100644 backend/bootstrap/cache/.gitignore create mode 100644 backend/bootstrap/providers.php create mode 100644 backend/composer.json create mode 100644 backend/composer.lock create mode 100644 backend/config/app.php create mode 100644 backend/config/auth.php create mode 100644 backend/config/cache.php create mode 100644 backend/config/database.php create mode 100644 backend/config/filesystems.php create mode 100644 backend/config/logging.php create mode 100644 backend/config/magic-starter.php create mode 100644 backend/config/mail.php create mode 100644 backend/config/queue.php create mode 100644 backend/config/services.php create mode 100644 backend/config/session.php create mode 100644 backend/database/.gitignore create mode 100644 backend/database/factories/UserFactory.php create mode 100644 backend/database/migrations/0001_01_01_000000_create_users_table.php create mode 100644 backend/database/migrations/0001_01_01_000001_create_cache_table.php create mode 100644 backend/database/migrations/0001_01_01_000002_create_jobs_table.php create mode 100644 backend/database/migrations/2026_06_24_100010_create_teams_table.php create mode 100644 backend/database/migrations/2026_06_24_100020_create_team_user_table.php create mode 100644 backend/database/migrations/2026_06_24_100030_create_team_invitations_table.php create mode 100644 backend/database/migrations/2026_06_24_100040_create_personal_access_tokens_table.php create mode 100644 backend/database/migrations/2026_06_24_100050_create_notifications_table.php create mode 100644 backend/database/migrations/2026_06_24_100060_create_notification_settings_table.php create mode 100644 backend/database/migrations/2026_06_24_100070_create_newsletter_subscribers_table.php create mode 100644 backend/database/migrations/2026_06_24_200010_add_current_team_id_to_users_table.php create mode 100644 backend/database/migrations/2026_06_24_200020_add_device_info_to_personal_access_tokens_table.php create mode 100644 backend/database/migrations/2026_06_24_200030_add_expires_at_to_team_invitations_table.php create mode 100644 backend/database/migrations/2026_06_24_200040_add_guest_and_phone_fields_to_users_table.php create mode 100644 backend/database/migrations/2026_06_24_200050_add_localization_fields_to_users_table.php create mode 100644 backend/database/migrations/2026_06_24_200060_add_profile_fields_to_users_table.php create mode 100644 backend/database/migrations/2026_06_24_200070_add_profile_photo_path_to_teams_table.php create mode 100644 backend/database/migrations/2026_06_24_200080_add_profile_photo_path_to_users_table.php create mode 100644 backend/database/migrations/2026_06_24_200090_add_timezone_to_users_table.php create mode 100644 backend/database/migrations/2026_06_24_200100_add_two_factor_columns_to_users_table.php create mode 100644 backend/database/migrations/2026_06_24_200110_drop_language_column_from_users_table.php create mode 100644 backend/database/seeders/DatabaseSeeder.php create mode 100644 backend/lang/vendor/magic-starter/en/teams.php create mode 100644 backend/lang/vendor/magic-starter/tr/teams.php create mode 100644 backend/package.json create mode 100644 backend/phpunit.xml create mode 100644 backend/public/.htaccess create mode 100644 backend/public/favicon.ico create mode 100644 backend/public/index.php create mode 100644 backend/public/robots.txt create mode 100644 backend/resources/css/app.css create mode 100644 backend/resources/js/app.js create mode 100644 backend/resources/views/welcome.blade.php create mode 100644 backend/routes/console.php create mode 100644 backend/routes/web.php create mode 100644 backend/storage/app/.gitignore create mode 100644 backend/storage/app/private/.gitignore create mode 100644 backend/storage/app/public/.gitignore create mode 100644 backend/storage/framework/.gitignore create mode 100644 backend/storage/framework/cache/.gitignore create mode 100644 backend/storage/framework/cache/data/.gitignore create mode 100644 backend/storage/framework/sessions/.gitignore create mode 100644 backend/storage/framework/testing/.gitignore create mode 100644 backend/storage/framework/views/.gitignore create mode 100644 backend/storage/logs/.gitignore create mode 100644 backend/tests/Feature/ExampleTest.php create mode 100644 backend/tests/TestCase.php create mode 100644 backend/tests/Unit/ExampleTest.php create mode 100644 backend/vite.config.js create mode 100644 bin/dispatcher.dart create mode 100755 bin/fsa create mode 100644 docs/component-registry.md create mode 100644 docs/design-culture/accessibility-wcag.md create mode 100644 docs/design-culture/apple-hig.md create mode 100644 docs/design-culture/material-design-3.md create mode 100644 docs/design-culture/motion-interaction.md create mode 100644 docs/design-culture/refactoring-ui.md create mode 100644 docs/design-culture/wind-responsive.md 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/_plugins.g.dart create mode 100644 lib/app/commands/_index.g.dart create mode 100644 lib/app/kernel.dart create mode 100644 lib/app/middleware/ensure_authenticated.dart create mode 100644 lib/app/middleware/redirect_if_authenticated.dart create mode 100644 lib/app/models/team.dart create mode 100644 lib/app/models/user.dart create mode 100644 lib/app/providers/app_service_provider.dart create mode 100644 lib/app/providers/route_service_provider.dart create mode 100644 lib/config/app.dart create mode 100644 lib/config/auth.dart create mode 100644 lib/config/broadcasting.dart create mode 100644 lib/config/cache.dart create mode 100644 lib/config/database.dart create mode 100644 lib/config/deeplink.dart create mode 100644 lib/config/logging.dart create mode 100644 lib/config/magic_starter.dart create mode 100644 lib/config/network.dart create mode 100644 lib/config/routing.dart create mode 100644 lib/config/view.dart create mode 100644 lib/config/wind_theme.g.dart create mode 100644 lib/main.dart create mode 100644 lib/preview/_previews.g.dart create mode 100644 lib/preview/components.preview.dart create mode 100644 lib/preview/dashboard_screen.preview.dart create mode 100644 lib/preview/foundations.preview.dart create mode 100644 lib/preview/login_screen.preview.dart create mode 100644 lib/preview/preview_mock_harness.dart create mode 100644 lib/preview/profile_screen.preview.dart create mode 100644 lib/preview/register_screen.preview.dart create mode 100644 lib/preview/screen_preview_scaffold.dart create mode 100644 lib/preview/settings_screen.preview.dart create mode 100644 lib/preview/teams_screen.preview.dart create mode 100644 lib/resources/views/dashboard_view.dart create mode 100644 lib/resources/views/welcome_view.dart create mode 100644 lib/routes/app.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/project.xcworkspace/xcshareddata/swiftpm/Package.resolved 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.xcworkspace/xcshareddata/swiftpm/Package.resolved 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/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 web/sqlite3.wasm 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/.claude/agents/component-visual-reviewer.md b/.claude/agents/component-visual-reviewer.md new file mode 100644 index 0000000..ec9924d --- /dev/null +++ b/.claude/agents/component-visual-reviewer.md @@ -0,0 +1,156 @@ +--- +name: component-visual-reviewer +description: "Scores a /preview screenshot pair (light + dark) against DESIGN.md tokens. Returns a numbered delta list with blocking and advisory items. Blocks on token violations." +tools: Read, Bash +--- + +# Component Visual Reviewer + +You are a visual design reviewer for the magic_example project. You score a component or screen screenshot pair against the `DESIGN.md` design system tokens and return a structured delta list. + +You do not self-grade code you just wrote. You are always invoked by an outside caller (another agent or the user) to review a screenshot that was produced by a separate step. + +--- + +## INPUTS + +You receive: + +- `screenshot_light`: path to a JPEG/PNG screenshot of the component in light mode +- `screenshot_dark`: path to a JPEG/PNG screenshot of the component in dark mode +- `design_md`: path to the DESIGN.md file (default: `magic_example/DESIGN.md`) +- `component`: name of the component or screen being reviewed + +--- + +## PROCESS + +### 1. Read DESIGN.md + +Read the `design_md` file to load: +- The `colors` section: light/dark hex values for every semantic token role. +- The `typography` section: font family, sizes, weights, line-heights. +- The `rounded` section: corner radius values. +- The `spacing` section: scale values. +- The `components` section: token bindings for specific components. + +### 2. Read the screenshots + +Read both screenshots visually. Identify: +- Background colors on each surface level. +- Text colors (primary, muted, disabled). +- Border colors. +- Spacing between elements. +- Corner radii on cards, inputs, buttons. +- Font family and approximate weights. +- Whether dark mode inverts as expected. + +### 3. Check the component source (optional but preferred) + +If the component source is accessible, read it to confirm token usage: + +```bash +find /Users/anilcan/Code/fluttersdk/magic_starter/lib/src/ui/components -name "*.dart" | xargs grep -l "" +``` + +Look for raw `Color(0xFF...)`, `Colors.*`, or hardcoded pixel margins that indicate a token bypass. + +```bash +grep -rn "Color(0x\|Colors\." /Users/anilcan/Code/fluttersdk/magic_starter/lib/src/ui/components// +grep -rn "SizedBox(height: [0-9]\|SizedBox(width: [0-9]" /Users/anilcan/Code/fluttersdk/magic_starter/lib/src/ui/components// +``` + +--- + +## SCORING DIMENSIONS + +Evaluate across five dimensions: + +### 1. Token Compliance (BLOCKING) + +- Background colors match `DESIGN.md` `colors` section (light and dark hex). +- Text colors match `fg`, `fg-muted`, `fg-disabled` roles. +- Border colors match `border` or `border-subtle` roles. +- Interactive element colors match `primary`, `on-primary`, `destructive`, etc. +- No raw hex or `Colors.*` visible in source code for this component. + +Any token violation is **blocking**: the delta MUST be fixed before shipping. + +### 2. Dark/Light Parity (BLOCKING if missing) + +- Dark mode screenshot is visually distinct from light mode. +- Surfaces that are light in light mode are dark in dark mode (and vice versa). +- Text that is dark in light mode is light in dark mode. +- No element is the same color in both modes (unless it is intentionally neutral, e.g. pure white icons on a brand-colored button). + +If light and dark screenshots look identical, the `dark:` counterpart token is missing. This is blocking. + +### 3. Layout and Spacing (advisory) + +- Spacing between elements matches the 4px scale from `DESIGN.md`. +- Touch targets are at least 44pt/48dp. +- Groups have more space between them than within them. +- Content does not fill the entire width when it needs less. + +### 4. Typography (advisory) + +- Font family is Inter (per DESIGN.md). +- Font sizes approximate the DESIGN.md type scale. +- Heading/body/caption hierarchy is visible. +- Line lengths are comfortable (not running the full screen width on wide layouts). + +### 5. Corner Radii (advisory) + +- Cards and dialogs use `lg` (16px) radius. +- Inputs and small controls use `DEFAULT` (8px) radius. +- Badges and pills use `full` (9999px) radius. +- Buttons use `md` (12px) radius. + +--- + +## OUTPUT FORMAT + +Return a numbered delta list. Mark each item as either `[BLOCKING]` or `[ADVISORY]`. + +``` +Component: +Mode: light + dark pair reviewed + +1. [BLOCKING] Token violation: background: light screenshot shows #F0F0F0 on the card surface; DESIGN.md `surface-container` is #F9FAFB. Check that `bg-surface-container` alias is applied, not a hardcoded palette utility. + +2. [BLOCKING] Dark mode missing: dark screenshot is visually identical to light screenshot. The card background does not invert. The alias `bg-surface-container` may not include its `dark:` counterpart. + +3. [ADVISORY] Spacing: the gap between the label and input (appears ~6px) is below the 8px minimum for related elements. Use `gap-2` (8px) minimum. + +4. [ADVISORY] Typography: caption text appears lighter than `text-fg-muted` tone in light mode; check that `Typography(variant: TypographyVariant.caption)` applies `text-fg-muted` correctly. + +5. [ADVISORY] Touch target: the close icon button appears to be ~32x32dp; add `min-h-11 min-w-11` to meet the 44dp minimum. +``` + +If there are no issues: + +``` +Component: +Mode: light + dark pair reviewed + +No deltas. Token compliance, dark/light parity, spacing, typography, and corner radii all match DESIGN.md. Approved. +``` + +--- + +## BLOCKING POLICY + +If any blocking item exists: +- State it clearly at the top: "BLOCKED: blocking item(s) found." +- The caller MUST fix all blocking items and re-invoke the reviewer before the component is considered ship-ready. +- Do not approve a component with a blocking delta, even if all advisory items are clean. + +--- + +## WHAT YOU DO NOT DO + +- Do not modify source files. +- Do not run the app or trigger hot reloads. +- Do not approve your own output (you are always reviewing a peer's work). +- Do not score items outside the five dimensions above. +- Do not make aesthetic judgments beyond token compliance (color preferences, layout choices beyond spacing rules, etc. are outside your scope). diff --git a/.claude/rules/design.md b/.claude/rules/design.md new file mode 100644 index 0000000..fea39d4 --- /dev/null +++ b/.claude/rules/design.md @@ -0,0 +1,109 @@ +--- +paths: + - "lib/**" +--- + +# Design Rules (UI surface) + +These rules apply whenever you touch any file under `lib/`. They complement `CLAUDE.md` with the implementation-level specifics. + +## Atomic Component Folder Contract + +Every component in the `magic_starter` generic library lives in a 4-file atomic folder: + +``` +magic_starter/lib/src/ui/components// + .dart # class extends StatelessWidget, @immutable + .recipe.dart # WindRecipe or WindSlotRecipe + .preview.dart # ONE preview widget rendering all variant x state combos + index.dart # exports .dart + .recipe.dart (NOT preview) +``` + +- Folder and file names are `lower_snake_case` (dotted suffixes `.recipe.dart`/`.preview.dart` are valid). +- Component class name is unprefixed `UpperCamelCase` (`card.dart` -> `class Card`). +- `index.dart` exports the component class, variant enums, and the recipe. Never export the preview file (it is dev-only). +- `previews:refresh` discovers components by scanning `*.preview.dart` files. One preview class per file, no exceptions. + +To scaffold: `dart run bin/dispatcher.dart make:component [--variants=intent,size] [--slots]` + +## WindRecipe Usage + +All variant logic lives in a `WindRecipe` (or `WindSlotRecipe` for slot-based components): + +```dart +final myRecipe = WindRecipe( + base: 'flex items-center gap-2', + variants: { + 'intent': { + 'primary': 'bg-primary text-on-primary', + 'secondary': 'bg-surface-container text-fg border border-color-border', + 'ghost': 'text-fg', + }, + 'size': { + 'sm': 'px-3 py-1.5 text-xs', + 'md': 'px-4 py-2 text-sm', + 'lg': 'px-5 py-2.5 text-base', + }, + }, + defaultVariants: {'intent': 'primary', 'size': 'md'}, +); +``` + +- Emission order is always: `base ++ variant (definition order) ++ compound ++ caller`. Never sort or deduplicate. +- Pass variant values as strings matching the map keys. Pass `null` to clear a default. +- The caller `className` argument appends last; it can override variant output at the same granularity. +- Import `WindRecipe` via `package:magic/magic.dart` inside `magic_starter` files (it re-exports the wind barrel). Direct `package:fluttersdk_wind/...` imports trip `depend_on_referenced_packages`. + +## Token-Only Rule + +All colors go through semantic alias keys defined in `DESIGN.md`. Never use raw hex, `Color(0xFF...)`, or `Colors.*` in component or view code. + +The 17 semantic alias keys: + +| Key | Role | +|-----|------| +| `bg-surface` | Page background | +| `bg-surface-container` | Card, panel background | +| `bg-surface-container-high` | Input background, nested panels | +| `text-fg` | Primary text | +| `text-fg-muted` | Secondary text | +| `text-fg-disabled` | Disabled/meta text | +| `bg-primary` | Brand action background | +| `text-on-primary` | Text on brand action surface | +| `bg-primary-container` | Tinted brand surface | +| `bg-accent` | Secondary accent | +| `border-color-border` | Dividers, card borders | +| `border-color-border-subtle` | Hairline borders | +| `bg-destructive` | Danger action background | +| `text-on-destructive` | Text on danger surface | +| `bg-destructive-container` | Tinted danger surface | +| `bg-success` | Success tone | +| `bg-warning` | Warning tone | + +Each alias expands to a `' dark:'` pair. Every `className` that sets a color MUST include the `dark:` counterpart. The alias system handles this automatically when you use the keys above. + +To add or change a semantic token: edit `DESIGN.md` and run `dart run bin/dispatcher.dart design:sync`. + +## Preview-Required Rule + +No component ships without a preview widget. + +- The preview file (`.preview.dart`) must render every variant x state combination so the catalog shows the full range. +- After adding or modifying a preview, regenerate the catalog: `dart run bin/dispatcher.dart previews:refresh` +- Verify dark/light parity by navigating to `/preview` in debug mode: `./bin/fsa dusk:navigate --route=/preview` +- Take light and dark screenshots and run the `component-visual-reviewer` agent (`.claude/agents/component-visual-reviewer.md`) before marking a component ship-ready. + +## Material Import Discipline + +Component files that share a name with a Material widget (`Card`, `Switch`, `Badge`, `Tooltip`, `Checkbox`, etc.) must import Flutter as: + +```dart +import 'package:flutter/widgets.dart'; +import 'package:flutter/material.dart' show Icons; // only if icons are needed +``` + +Never `import 'package:flutter/material.dart'` without `show`. Build exclusively on Wind W-widgets inside component bodies. + +## Release Boundary + +Preview files (`*.preview.dart`) and the generated `_previews.g.dart` are dev-only. They are excluded from release builds through the `magic_devtools` dev-package boundary and `kDebugMode`/`kReleaseMode` const-fold. Never import a preview file from production code. diff --git a/.claude/skills/design-first-workflow/SKILL.md b/.claude/skills/design-first-workflow/SKILL.md new file mode 100644 index 0000000..ccfdd23 --- /dev/null +++ b/.claude/skills/design-first-workflow/SKILL.md @@ -0,0 +1,207 @@ +--- +name: design-first-workflow +description: "End-to-end design-first loop for building screens and composing components: design token binding -> scaffold -> preview dark/light -> compose -> wire to controller -> dusk verify. Encodes the CREATE->SCREENSHOT->ANALYZE->FIX->VERIFY self-heal cycle with hard limits." +when_to_use: "TRIGGER when: building a new screen, composing existing components into a view, wiring a view to a controller, or running a full design-first verification pass." +--- + +# Design-First Workflow + +The end-to-end loop for building a screen or view in this project: from DESIGN.md token decisions to a dusk-verified, dark/light-parity-confirmed, controller-wired result. + +--- + +## THE LOOP + +``` +DESIGN -> SCAFFOLD -> PREVIEW -> COMPOSE -> WIRE -> VERIFY +``` + +For iterative fixes after the first preview: + +``` +SCREENSHOT -> ANALYZE -> FIX -> VERIFY +maximum 3 rounds; stop if no improvement across a full round +``` + +--- + +## PHASE 1: DESIGN + +Read `magic_example/DESIGN.md` before touching any code. + +Decide: +1. Which semantic tokens govern this screen (background, text, interactive elements). +2. Which components from `docs/component-registry.md` cover the UI needs. +3. The visual hierarchy: what is primary, secondary, tertiary on this screen. +4. Responsive behavior: narrow (single column) vs `md` and wider (sidebar + content, or row layout). + +Do NOT start coding until the token bindings are clear. A screen that uses the right tokens will automatically update when the brand changes; a screen with hardcoded hex will not. + +--- + +## PHASE 2: SCAFFOLD (new screens) + +For a new standalone screen, create the view file: + +```sh +dart run bin/dispatcher.dart make:component Preview [--slots] +``` + +Or author the view file directly if it follows an existing auth/profile/settings pattern. Views live under `magic_starter/lib/src/ui/views//`. Components live under `magic_starter/lib/src/ui/components//`. + +For a new component within the screen, follow the `make-component` skill. + +--- + +## PHASE 3: PREVIEW (dark + light) + +Every screen must be previewed in both light and dark before wiring to a controller. + +Start the app and navigate to the preview catalog: + +```sh +./bin/fsa start --device=chrome +./bin/fsa dusk:navigate --route=/preview +``` + +Capture both modes: + +```sh +# Light mode +./bin/fsa dusk:screenshot -o /tmp/-light.jpg + +# Toggle to dark mode via the catalog header toggle, then: +./bin/fsa dusk:screenshot -o /tmp/-dark.jpg +``` + +Dark and light screenshots MUST be visually distinct. If they look identical, a semantic token alias is missing its `dark:` counterpart. + +--- + +## PHASE 4: COMPOSE + +Compose the screen from library components. Rules: + +- Use `magic_starter` components from `docs/component-registry.md`. Do not write one-off inline widgets when a library component fits. +- Token discipline: `bg-surface`, `text-fg`, `border-color-border`, etc. Never `Colors.grey.shade200`. +- Layout: `WDiv` with `flex-col` or `flex-row`; Wind breakpoint prefixes for responsive behavior. +- Interactive elements: `Button`, `Input`, `Checkbox`, `Switch` from the component library; not raw `WButton`/`WInput` unless the component library explicitly wraps them. + +--- + +## PHASE 5: WIRE TO CONTROLLER + +Wire the screen to its `MagicStatefulView`: + +1. The view is a `MagicStatefulView` or `MagicView`. +2. The controller extends `MagicController` + `MagicStateMixin`. +3. All data flows through the controller; the view reads `controller.state` and calls controller methods. +4. API calls use `Http.post/get/put/delete`; error handling via `handleApiError`. +5. Gate-checked UI elements use `Auth.can('ability')` (advisory; backend enforces). + +Preserve the view-registry key (`'auth.login'`, `'profile.settings'`, etc.) unchanged. + +--- + +## PHASE 6: VERIFY (self-heal loop) + +Run the full self-heal cycle after wiring: + +### CREATE + +Navigate to the screen in the running app: + +```sh +./bin/fsa dusk:navigate --route=/ +``` + +Or use the `/preview` catalog for isolated component testing. + +### SCREENSHOT + +Capture screenshots for both modes: + +```sh +./bin/fsa dusk:screenshot -o /tmp/-light.jpg +# toggle dark, then: +./bin/fsa dusk:screenshot -o /tmp/-dark.jpg +``` + +### ANALYZE + +Invoke the `component-visual-reviewer` subagent: + +``` +Agent({subagent_type: "ac:component-visual-reviewer"}) with: + - screenshot_light: /tmp/-light.jpg + - screenshot_dark: /tmp/-dark.jpg + - design_md: magic_example/DESIGN.md + - component: +``` + +The reviewer returns: +- A numbered delta list (layout, spacing, token compliance, dark/light parity, typography). +- **Blocking** items: token violations (wrong color, missing dark variant, raw hex visible in screenshot). These must be fixed before proceeding. +- **Advisory** items: spacing, layout, typography notes. Fix if straightforward; log the rest. + +### FIX + +Address each blocking delta: +- Token violation: replace the non-token value with the correct semantic alias. +- Missing dark counterpart: add `dark:` class to the alias (or check that `design:sync` generated the alias correctly). +- Wrong component: swap to the correct library component. + +After fixing, regenerate if needed: + +```sh +dart run bin/dispatcher.dart previews:refresh +./bin/fsa reload +``` + +### VERIFY + +Re-screenshot and re-analyze. Repeat the cycle. + +**Stop conditions:** +- All blocking items are resolved. +- No improvement across a full round (three rounds maximum; escalate if still failing after round 3). + +--- + +## RULES + +- Read `DESIGN.md` before any token decision. +- Read `docs/component-registry.md` before building a component. +- Preview dark + light before wiring to a controller. +- Maximum 3 self-heal rounds per screen or component. +- Stop on no-improvement: if the delta list does not shrink after a round, escalate rather than looping. +- Token violations are always blocking; layout/spacing notes are advisory. +- `make:component` + `previews:refresh` are the only ways to add components to the catalog. Do not manually edit `_previews.g.dart`. +- Do not wire a screen to a real backend during preview; use mock controllers or `Http.fake()` stubs. + +--- + +## COMMANDS REFERENCE + +| Command | Phase | What it does | +|---------|-------|-------------| +| `dart run bin/dispatcher.dart make:component ` | SCAFFOLD | Scaffold atomic folder + chain previews:refresh | +| `dart run bin/dispatcher.dart previews:refresh` | SCAFFOLD/FIX | Regenerate preview registry | +| `dart run bin/dispatcher.dart design:sync` | DESIGN | Regenerate Wind theme from DESIGN.md | +| `dart run bin/dispatcher.dart design:lint` | DESIGN | Validate DESIGN.md | +| `./bin/fsa start --device=chrome` | PREVIEW | Boot app for dusk interaction | +| `./bin/fsa dusk:navigate --route=/preview` | PREVIEW | Open preview catalog | +| `./bin/fsa dusk:screenshot -o ` | SCREENSHOT | Capture current app state | +| `./bin/fsa reload` | FIX | Hot reload after changes | + +--- + +## REFERENCES + +- `magic_example/DESIGN.md` token source +- `docs/component-registry.md` component inventory +- `.claude/skills/frontend-design/SKILL.md` token/color/type/spacing guidance +- `.claude/skills/make-component/SKILL.md` component scaffold detail +- `.claude/agents/component-visual-reviewer.md` visual review subagent +- `docs/design-culture/refactoring-ui.md` hierarchy and spacing principles +- `docs/design-culture/wind-responsive.md` breakpoints and layout patterns diff --git a/.claude/skills/frontend-design/SKILL.md b/.claude/skills/frontend-design/SKILL.md new file mode 100644 index 0000000..55828d1 --- /dev/null +++ b/.claude/skills/frontend-design/SKILL.md @@ -0,0 +1,357 @@ +--- +name: frontend-design +description: "Flutter/Wind/Magic-native UI design skill: design systems, visual hierarchy, bold aesthetics, and semantic token usage for this project. Covers component authoring, DESIGN.md-driven theming, and dark/light parity. Use for any UI, component, or screen work in magic_example." +when_to_use: "TRIGGER when: UI, pages, components, screens, design. DO NOT TRIGGER when: backend, auth logic, or non-visual." +--- + +# Frontend Design (Flutter/Wind/Magic) + +Production-grade UI design for Flutter apps built on the Wind utility system. This is a project-specific fork of the generic frontend-design skill, pinned to MOBILE/Flutter mode. All guidance is Wind className and WindRecipe based; web/CSS-only directives have been reconciled or removed. + +--- + +## MODE + +This skill is **MOBILE/Flutter only.** There is no web/CSS mode for this project. + +Platform: Flutter (mobile-first, responsive via Wind breakpoint prefixes). +Styling: Wind className strings only. No raw `Colors.*`, no hardcoded hex in component code. +Theme: `DESIGN.md` is the single source of truth for all tokens. See the `colors`, `typography`, `rounded`, and `spacing` sections. +Components: use the project component library (`magic_starter` generic component set). Never build inline one-offs when a library component covers the case. + +--- + +## DESIGN PROCESS (BEFORE CODING) + +Before writing any widget code, commit to a bold aesthetic direction by answering four questions: + +1. **Purpose**: What problem does this screen or component solve? Who uses it? +2. **Tone**: Choose a clear direction and commit fully (brutally minimal, refined precision, warm/approachable, editorial, etc.). +3. **Constraints**: Touch targets, safe areas, responsive breakpoints (`sm`/`md`/`lg` via Wind), accessibility (4.5:1 WCAG AA). +4. **Differentiation**: What is the one thing a user will remember about this surface? + +Then implement working code that is production-grade, visually distinctive, and cohesive. + +### Design-First Workflow + +1. Design the actual piece of functionality first, not the navigation shell. +2. Work in grayscale first; add color after hierarchy is clear. +3. Establish token bindings (spacing, type, color via semantic aliases) before detailed styling. +4. Iterate in cycles; details come last. + +For the full workflow loop (including screenshot + verify), see the `design-first-workflow` skill. + +--- + +## DESIGN SYSTEMS + +### Spacing Scale + +Defer to `DESIGN.md`'s `spacing` section and the Wind utility scale. The project 4px logical scale: + +| Token | Size | Wind class | Use case | +|-------|------|-----------|----------| +| xs | 4px | `p-1` / `gap-1` | Micro gaps, icon padding | +| sm | 8px | `p-2` / `gap-2` | Within components | +| md | 16px | `p-4` / `gap-4` | Standard screen padding | +| lg | 24px | `p-6` / `gap-6` | Between sections | +| xl | 40px | `p-10` / `gap-10` | Major separation | +| 2xl | 64px | `p-16` / `gap-16` | Hero areas | +| gutter | 16px | `px-4` | Horizontal content margin (narrow screens) | +| section | 32px | `py-8` | Stacked section separation | + +Do not use arbitrary pixel values (`p-[13px]`); stay on the 4px scale. For semantic spacing tokens, use the alias keys from `magic_example/DESIGN.md`. + +### Type Scale + +Typography is **Inter** (the authoritative font from `DESIGN.md`). Use the `Typography` component or Wind text utilities matching the DESIGN.md scale. All sizes are logical pixels. + +| DESIGN.md token | Wind approx | Role | +|----------------|-------------|------| +| `label-sm` | `text-xs` | Captions, meta, timestamps | +| `body-md` | `text-sm` | Default body text | +| `body-lg` | `text-base` | Emphasized body | +| `title-lg` | `text-lg` | Card/section titles | +| `headline-md` | `text-xl` | Subheadings | +| `headline-lg` | `text-2xl` | Screen titles | +| `display` | `text-3xl` | Hero/display text | + +Line-height and letter-spacing from DESIGN.md apply; they are already encoded in the `Typography` component recipe. + +**Font selection for this project**: Inter is the authoritative app font per `DESIGN.md`. The generic fork's "never Inter/Roboto/system-ui" rule does NOT apply here. DESIGN.md typography is always authoritative over general font guidance. + +### Shadow and Elevation + +Wind does not support CSS `box-shadow` or `filter` utilities (part of the ~72 unsupported CSS families). Express depth through: + +- Background tonal shifts: `bg-surface` -> `bg-surface-container` -> `bg-surface-container-high` +- Subtle border lines: `border border-color-border` +- Use the `WindRecipe` `shadow-sm`/`shadow-md` tokens only if the consumer WindThemeData has them aliased; otherwise rely on tonal backgrounds. + +For elevation semantics, see [docs/design-culture/material-design-3.md](../../docs/design-culture/material-design-3.md). + +### Transforms and Filters + +Wind does not support CSS `transform`, `rotate`, `scale`, `translate`, `filter`, `backdrop-filter`, `group-*`, or `peer-*` utilities. For motion and transitions, use Flutter's animation system directly (`AnimatedContainer`, `AnimatedOpacity`, `TweenAnimationBuilder`), not Wind className strings. + +--- + +## VISUAL HIERARCHY + +Every element sits at one of three levels: + +- **Primary**: `text-fg` + heavy weight (`font-bold`) headlines, key actions (one per section) +- **Secondary**: `text-fg-muted` supporting text, dates, descriptions +- **Tertiary**: `text-fg-disabled` metadata, timestamps, copyright + +### Key Principles + +- Size is not everything: use weight and color before increasing font size. +- **Emphasize by de-emphasizing**: soften competing elements instead of loudening the target. +- Labels are a last resort: combine with values ("12 left in stock" beats "Stock: 12"). +- Icons are visually heavy: give them `text-fg-muted` or `text-fg-disabled` to balance with text. + +### Button Hierarchy + +| Level | Wind recipe | Rule | +|-------|-------------|------| +| Primary | `Button(intent: ButtonIntent.primary)` | One per section maximum | +| Secondary | `Button(intent: ButtonIntent.secondary)` | Clear but not competing | +| Ghost | `Button(intent: ButtonIntent.ghost)` | Discoverable, unobtrusive | +| Destructive | `Button(intent: ButtonIntent.destructive)` | Only on destructive actions | + +Destructive actions do not have to be big/red/bold on all screens. On regular content pages where delete is secondary, use ghost or secondary styling. Reserve full destructive styling for confirmation dialogs. + +--- + +## COLOR SYSTEM + +### Use Semantic Tokens, Not Hex + +All color decisions go through semantic alias tokens defined in `DESIGN.md`. Never put raw hex or `Colors.*` in component code. + +| Role | Wind alias | Use | +|------|-----------|-----| +| `bg-surface` | Page background | | +| `bg-surface-container` | Card, panel background | | +| `bg-surface-container-high` | Input background, nested panels | | +| `text-fg` | Primary text | | +| `text-fg-muted` | Secondary text | | +| `text-fg-disabled` | Disabled/meta text | | +| `bg-primary` / `text-on-primary` | Brand action / on-brand text | | +| `bg-primary-container` | Tinted brand surface | | +| `bg-accent` | Secondary accent | | +| `border-color-border` | Dividers, card borders | | +| `border-color-border-subtle` | Hairline borders | | +| `bg-destructive` / `text-on-destructive` | Danger action / on-danger text | | +| `bg-success` / `bg-warning` | Status tones | | + +For arbitrary-hex aliases generated by `design:sync`, Wind expands them as arbitrary-value utilities: `bg-[#7C3AED] dark:bg-[#8B5CF6]`. + +### Dark/Light Parity + +Every Wind className that carries a color token MUST include its `dark:` counterpart. This is enforced by the alias system: each alias key expands to a light+dark pair (e.g. `bg-surface` -> `bg-white dark:bg-[#030712]`). Never set a background or text color without a `dark:` override. + +Use the `/preview` catalog and the `component-visual-reviewer` subagent to verify dark/light parity before shipping. + +### Accessibility + +| Text type | Minimum contrast | Checked by | +|-----------|-----------------|-----------| +| Normal text (<18px) | 4.5:1 | `design:lint` | +| Large text (18px+ bold or 24px+) | 3:1 | `design:lint` | + +Never rely on color alone for meaning. Add icons, text, or patterns alongside color cues. + +--- + +## TYPOGRAPHY + +Inter is the project font (see `DESIGN.md` typography section). Use the `Typography` component for all text rendering rather than raw `WText` with ad-hoc sizes. + +### Line-Height and Spacing + +- Small text: taller line-height (1.5-2.0 equivalent). +- Large headlines: shorter line-height (1.0-1.2 equivalent). +- These are already encoded in the DESIGN.md `typography` entries; use them as-is. + +### Alignment + +- Default: left-aligned. +- Center: only for headlines and short blocks (under 2-3 lines). +- Right-align numbers in column comparison contexts. + +--- + +## LAYOUT AND SPACING + +### Mobile-First + +Design for narrow screens first. Use Wind breakpoint prefixes to expand at `sm` (640px) and `md` (768px): + +``` +// narrow: stacked columns +// md and wider: row layout +WDiv(className: 'flex flex-col md:flex-row gap-4') +``` + +### Touch Targets + +| Minimum | Comfortable | +|---------|------------| +| 44x44 pt (iOS) | 48x48 dp (Android) | + +Add invisible padding if an icon or label is smaller than the minimum target. Use `min-h-11 min-w-11` as a floor. + +### Safe Areas + +Respect device notches and home indicators via Flutter's `SafeArea` widget. Never place interactive elements in unsafe areas. + +### Navigation Patterns + +| Pattern | Wind/Magic component | Use case | +|---------|---------------------|---------| +| Bottom navigation | `Navbar` | 3-5 primary destinations | +| Tab bar | `Tabs` | Content categories | +| Navigation drawer | `AppLayout` sidebar | Many destinations | +| Bottom sheet | `BottomSheet` | Contextual actions | + +### Spacing Discipline + +More space between groups than within groups: +- Form labels sit closer to their input than to the preceding element. +- Section headings have more space above than below. +- List items within a group are tighter than the group gap. + +--- + +## DEPTH AND MOTION + +### Tonal Depth (Wind-compatible) + +Wind does not support `box-shadow` or `filter`. Express depth through tonal backgrounds: + +- Raised: use a lighter background (`bg-surface-container`) against the page (`bg-surface`). +- Inset: use a darker/deeper background (`bg-surface-container-high`) for inputs and nested panels. + +### Motion + +Flutter animation system handles motion; Wind className strings do not carry transitions/transforms. Focus on high-impact moments: + +- Page transitions: use `MagicRoute` transition settings, not custom animations per-view. +- State changes (loading/error): use the `Skeleton` component for loading states; avoid inline spinners. +- Reduced motion: respect `MediaQuery.of(context).disableAnimations` in all custom animations. + +For detailed easing/duration guidance, see [docs/design-culture/motion-interaction.md](../../docs/design-culture/motion-interaction.md). + +--- + +## SPATIAL COMPOSITION + +- Asymmetry and unexpected layouts can add character; do not default to symmetric grids. +- Generous negative space reads as premium; controlled density reads as rich/capable. +- Avoid filling the whole screen when the content only needs part of it. + +--- + +## MOBILE-SPECIFIC PATTERNS + +### Loading States + +Use the `Skeleton` component (preferred over spinners). Shape variants: `block`, `text`, `circle`. + +### Empty States + +Use the `EmptyState` component: +- Illustration or icon to grab attention. +- Clear title and helpful description. +- A call-to-action `Button`. +- Hide irrelevant UI (filters, tabs) when they have no effect yet. + +### Forms + +- Full-width `Input` with horizontal `gutter` padding. +- `FormField` handles label + hint + error state (never roll inline). +- Primary action: full-width `Button(intent: ButtonIntent.primary)` at the bottom. +- Error state: `border-color-border` turns `border-color-destructive`; use `FormField`'s error slot. + +--- + +## ANTI-PATTERNS + +| Anti-pattern | Fix | +|-------------|-----| +| Raw `Color(0xFF...)` or `Colors.*` in component code | Use Wind semantic token alias | +| Hardcoded pixel values (`SizedBox(height: 13)`) | Use Wind spacing utilities on the 4px scale | +| Font family chosen outside DESIGN.md | DESIGN.md typography is authoritative; Inter is the font | +| Purple gradients on white without dark counterpart | Every color token needs its `dark:` pair | +| Filling the whole screen when content needs less | Add `max-w-*` or `mx-auto`; let content breathe | +| Ambiguous spacing between groups | More space between groups than within | +| Touch targets under 44pt/48dp | Add `min-h-11 min-w-11` or invisible padding | +| Ignoring safe areas | Wrap top-level screens in `SafeArea` | +| Color as sole communication channel | Add icon, text, or pattern alongside color | +| Multiple previews in one file | One preview class per `*.preview.dart` file | +| CSS-only utilities (`box-shadow`, `filter`, `transform`, `group-*`) | These are unsupported by Wind; use Flutter APIs instead | +| `Icons.*` inline in component bodies | Extract as `static const IconData _icon = Icons.x;` | +| Skipping dark/light parity | Every semantic token alias is a light+dark pair; no exceptions | +| Building one-off widgets when a library component exists | Always check `magic_starter` component library first | + +--- + +## COMPONENT AUTHORING + +When building a new component, follow the atomic folder convention: + +``` +magic_starter/lib/src/ui/components// + .dart # class extends StatelessWidget + .recipe.dart # WindRecipe / WindSlotRecipe + .preview.dart # single preview widget (ONE per file) + index.dart # barrel: export .dart + .recipe.dart (NOT preview) +``` + +Generate the scaffold with: + +```sh +dart run bin/dispatcher.dart make:component [--variants=intent,size] [--slots] +``` + +This chains `previews:refresh` automatically. + +Every new component needs: +1. A `WindRecipe` or `WindSlotRecipe` in the recipe file. +2. Semantic token classNames only (from `DESIGN.md` alias set). +3. A preview widget rendering every variant x state combination. +4. Tests asserting the rendered `WDiv.className` for each variant. + +For the full component lifecycle (design -> scaffold -> preview -> verify), load the `make-component` skill. + +--- + +## OUTPUT GUIDANCE + +When generating UI code, structure output as: + +1. **Design direction** 1-2 sentences on aesthetic approach and key token decisions. +2. **Code** complete, working Flutter/Wind implementation. +3. **Responsive notes** breakpoint behavior if applicable (narrow -> `md` -> wider). + +Lead with code, not explanation. + +--- + +## REFERENCES + +| Topic | File | +|-------|------| +| DESIGN.md (token source) | `magic_example/DESIGN.md` | +| Visual hierarchy | `docs/design-culture/refactoring-ui.md` | +| Color system + contrast | `docs/design-culture/accessibility-wcag.md` | +| Mobile patterns + safe areas | `docs/design-culture/wind-responsive.md` | +| Material 3 role semantics | `docs/design-culture/material-design-3.md` | +| Motion + easing | `docs/design-culture/motion-interaction.md` | +| Apple HIG / iOS patterns | `docs/design-culture/apple-hig.md` | +| Component authoring workflow | `.claude/skills/make-component/SKILL.md` | +| Design-first end-to-end loop | `.claude/skills/design-first-workflow/SKILL.md` | +| Visual reviewer subagent | `.claude/agents/component-visual-reviewer.md` | +| Component registry | `docs/component-registry.md` | diff --git a/.claude/skills/make-component/SKILL.md b/.claude/skills/make-component/SKILL.md new file mode 100644 index 0000000..11ad12c --- /dev/null +++ b/.claude/skills/make-component/SKILL.md @@ -0,0 +1,198 @@ +--- +name: make-component +description: "Scaffold, preview, and verify a new magic_starter component using the design->scaffold->preview->verify loop. Covers make:component, previews:refresh, /preview catalog navigation, dusk screenshot capture, and the component-visual-reviewer sign-off." +when_to_use: "TRIGGER when: creating a new component, adding a variant to an existing component, or verifying a component's visual output." +--- + +# Make Component + +The full lifecycle for authoring a component in this project: from design intent to a reviewed, catalog-visible, token-compliant widget. + +--- + +## OVERVIEW + +Every component follows the atomic 4-file folder shape: + +``` +magic_starter/lib/src/ui/components// + .dart # class extends StatelessWidget, @immutable + .recipe.dart # WindRecipe or WindSlotRecipe + .preview.dart # ONE preview widget rendering all variants + index.dart # exports .dart + .recipe.dart; NOT the preview +``` + +The lifecycle is: **DESIGN -> SCAFFOLD -> PREVIEW -> VERIFY**. + +--- + +## STEP 1: DESIGN + +Before scaffolding, decide: + +- **Component name** (PascalCase, no prefix): `Button`, `Card`, `Badge`, etc. +- **Variant axes** (intent, size, tone, shape, etc.) and their values. +- **Slots** (if the component composes named child regions: `trigger`, `panel`, `header`, `footer`). +- **Token bindings**: which semantic alias keys (`bg-primary`, `text-fg`, `border-color-border`, etc.) map to which visual role. + +Consult `magic_example/DESIGN.md` for the authoritative token set. Consult `docs/component-registry.md` to avoid duplicating an existing component. + +--- + +## STEP 2: SCAFFOLD + +Generate the 4-file folder with: + +```sh +dart run bin/dispatcher.dart make:component [--variants=intent,size] [--slots] +``` + +This command: +1. Creates `magic_starter/lib/src/ui/components//` with the 4 files. +2. Populates the recipe with the requested variant axes. +3. Automatically chains `previews:refresh` so the new preview is registered. + +After scaffolding, edit the generated files: + +- `.recipe.dart`: fill in token classNames for each variant value. Use semantic aliases only (`bg-primary`, `text-on-primary`, `bg-surface-container`, etc.). +- `.dart`: build the widget body using Wind W-widgets (`WDiv`, `WText`, `WButton`, etc.). Import via `package:magic/magic.dart` (re-exports the full wind barrel). +- `.preview.dart`: render a variant matrix (every combination of variant axes + dark/light). Keep ONE preview class per file. +- `index.dart`: verify the exports include the component class and any variant enums, but NOT the preview class. + +If you add the component to the barrel, export it from `magic_starter/lib/magic_starter.dart`: + +```sh +# After editing, regenerate the preview registry: +dart run bin/dispatcher.dart previews:refresh +``` + +--- + +## STEP 3: PREVIEW + +Open the preview catalog to inspect the component visually: + +```sh +# In one terminal: start the app +./bin/fsa start --device=chrome + +# In another terminal: navigate to the preview catalog +./bin/fsa dusk:navigate --route=/preview +``` + +The `/preview` catalog lists all registered previews. Each `*.preview.dart` produces one entry. The catalog provides a global dark/light toggle to check both modes. + +If the component is not listed, run `previews:refresh` again and hot-reload: + +```sh +dart run bin/dispatcher.dart previews:refresh +./bin/fsa reload +``` + +--- + +## STEP 4: SCREENSHOT + +Capture screenshots for visual review: + +```sh +# Light mode screenshot +./bin/fsa dusk:screenshot -o /tmp/-light.jpg + +# Toggle to dark (tap the theme toggle in the catalog header), then: +./bin/fsa dusk:screenshot -o /tmp/-dark.jpg +``` + +Both files must be non-empty and visually distinct (dark != light). + +--- + +## STEP 5: VERIFY (component-visual-reviewer) + +Invoke the `component-visual-reviewer` subagent to score the screenshot pair against `DESIGN.md` tokens: + +``` +Agent({subagent_type: "ac:component-visual-reviewer"}) with: + - screenshot_light: /tmp/-light.jpg + - screenshot_dark: /tmp/-dark.jpg + - design_md: magic_example/DESIGN.md + - component: +``` + +The reviewer returns a numbered delta list. Any token violation (wrong color, missing dark variant, hardcoded hex visible) is blocking. Layout and spacing deltas are advisory. + +Fix each blocking delta, re-run `previews:refresh`, hot-reload, re-screenshot, and re-verify. Maximum 3 rounds; stop if no improvement across a full round. + +--- + +## RECIPE AUTHORING RULES + +- Emission order is `base ++ variant(definition order) ++ compound(array order) ++ caller`. Never change this. +- Override at the same token granularity: use `px-4` to override `px-*`, not `p-*` overriding `px-*`. +- `defaultVariants` sets the fallback when the caller does not specify an axis. +- Pass enum values as `.name` strings: `ButtonIntent.primary.name` -> `'primary'`. +- Compound variants fire when multiple axes match simultaneously. +- The `className` override parameter allows the caller to bypass the recipe entirely (escape hatch; keep it). + +Example WindRecipe shape: + +```dart +final buttonRecipe = WindRecipe( + base: 'inline-flex items-center justify-center font-semibold rounded-md transition-colors', + variants: { + 'intent': { + 'primary': 'bg-primary text-on-primary', + 'secondary': 'bg-surface-container text-fg border border-color-border', + 'ghost': 'bg-transparent text-fg-muted', + 'destructive': 'bg-destructive text-on-destructive', + }, + 'size': { + 'sm': 'text-xs px-3 py-1.5', + 'md': 'text-sm px-4 py-2', + 'lg': 'text-base px-6 py-3', + }, + }, + defaultVariants: {'intent': 'primary', 'size': 'md'}, +); +``` + +--- + +## MIGRATING AN EXISTING WIDGET + +If the component replaces an existing `MagicStarter*` widget: + +1. Grep the codebase for all callers and existing className-asserting tests. +2. Write a baseline helper (`legacyXClassName(...)`) asserting the recipe output is byte-identical to the old pinned strings BEFORE changing the widget body (TDD red phase). +3. After the recipe is green, move the widget body to call the recipe. +4. Keep the old widget name as a thin re-export alias so callers and tests remain untouched: + +```dart +// magic_starter_button.dart (old location) +export 'components/button/button.dart' show Button, ButtonIntent; +``` + +Never weaken existing assertions to make them pass. + +--- + +## COMMANDS REFERENCE + +| Command | What it does | +|---------|-------------| +| `dart run bin/dispatcher.dart make:component [--variants=a,b] [--slots]` | Scaffold 4-file atomic folder + chain previews:refresh | +| `dart run bin/dispatcher.dart previews:refresh` | Regenerate `_previews.g.dart` from all `*.preview.dart` files | +| `dart run bin/dispatcher.dart design:sync` | Regenerate Wind theme from `DESIGN.md` | +| `dart run bin/dispatcher.dart design:lint` | Validate `DESIGN.md` against design rules | +| `./bin/fsa dusk:navigate --route=/preview` | Open the preview catalog in the running app | +| `./bin/fsa dusk:screenshot -o ` | Capture a screenshot of the running app | + +--- + +## REFERENCES + +- `magic_example/DESIGN.md` token source +- `docs/component-registry.md` component inventory and anti-patterns +- `.claude/skills/frontend-design/SKILL.md` token/color/type guidance +- `.claude/skills/design-first-workflow/SKILL.md` end-to-end loop for composing screens +- `.claude/agents/component-visual-reviewer.md` visual review subagent diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..e2c21dc --- /dev/null +++ b/.env.example @@ -0,0 +1,6 @@ +APP_NAME= +APP_ENV= +APP_DEBUG= +APP_KEY= + +API_URL= diff --git a/.metadata b/.metadata new file mode 100644 index 0000000..cf4b47a --- /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: "c9a6c484230f8b5e408ec57be1ef71dee1e77020" + channel: "stable" + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: c9a6c484230f8b5e408ec57be1ef71dee1e77020 + base_revision: c9a6c484230f8b5e408ec57be1ef71dee1e77020 + - platform: android + create_revision: c9a6c484230f8b5e408ec57be1ef71dee1e77020 + base_revision: c9a6c484230f8b5e408ec57be1ef71dee1e77020 + - platform: ios + create_revision: c9a6c484230f8b5e408ec57be1ef71dee1e77020 + base_revision: c9a6c484230f8b5e408ec57be1ef71dee1e77020 + - platform: linux + create_revision: c9a6c484230f8b5e408ec57be1ef71dee1e77020 + base_revision: c9a6c484230f8b5e408ec57be1ef71dee1e77020 + - platform: macos + create_revision: c9a6c484230f8b5e408ec57be1ef71dee1e77020 + base_revision: c9a6c484230f8b5e408ec57be1ef71dee1e77020 + - platform: web + create_revision: c9a6c484230f8b5e408ec57be1ef71dee1e77020 + base_revision: c9a6c484230f8b5e408ec57be1ef71dee1e77020 + - platform: windows + create_revision: c9a6c484230f8b5e408ec57be1ef71dee1e77020 + base_revision: c9a6c484230f8b5e408ec57be1ef71dee1e77020 + + # 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/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..97ff2b2 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,92 @@ +# Agent Guidance + +Behavioral contract for AI agents (Claude, Cursor, Copilot, etc.) working in this project. + +## Before Any UI Work + +1. Read `DESIGN.md` (loaded automatically via `@DESIGN.md` in `CLAUDE.md`). +2. Read the relevant `docs/design-culture/` file for the surface you are building (loaded automatically via `@docs/design-culture/`). +3. Check `docs/component-registry.md` to see whether a component already exists for the UI need. +4. Load `.claude/skills/frontend-design/SKILL.md` for all component and screen work. + +Do not write a single widget until token bindings are decided. A screen built on the right semantic tokens will adapt to any brand change automatically; a screen with hardcoded hex will not. + +## Visual Self-Heal Loop + +For every new component or screen, run this loop after the first implementation: + +``` +CREATE -> SCREENSHOT -> ANALYZE -> FIX -> VERIFY +``` + +**Maximum 3 rounds.** Stop if no improvement is observed across a full round. + +### Steps + +**CREATE**: implement the component or screen using semantic tokens and library components. + +**SCREENSHOT**: capture light and dark screenshots. + +```sh +# Navigate to the component in the preview catalog +./bin/fsa dusk:navigate --route=/preview + +# Light mode screenshot +./bin/fsa dusk:screenshot -o .ac/evidence/-light.png + +# Switch to dark mode in the preview catalog, then: +./bin/fsa dusk:screenshot -o .ac/evidence/-dark.png +``` + +**ANALYZE**: invoke the `component-visual-reviewer` subagent (`.claude/agents/component-visual-reviewer.md`) with both screenshots and the component name. The reviewer scores token compliance, dark/light parity, spacing, typography, and corner radii. + +**FIX**: address every BLOCKING item from the reviewer. ADVISORY items are addressed if they can be fixed without scope creep. + +**VERIFY**: re-screenshot and re-run the reviewer. If all BLOCKING items are resolved, the component is ship-ready. + +If the reviewer returns the same BLOCKING items after 3 rounds with no progress, stop and surface the issue to the user. + +## Component Registry + +Before scaffolding, always check `docs/component-registry.md`. It maps every available component to its variants, token bindings, and anti-patterns. If the registry has a component that covers the need, use it. + +For new components: + +```sh +dart run bin/dispatcher.dart make:component [--variants=intent,size] [--slots] +``` + +This scaffolds the 4-file atomic folder under `magic_starter/lib/src/ui/components//` and chains `previews:refresh`. + +## Skills to Load + +| Situation | Skill | +|-----------|-------| +| Any UI, component, or screen work | `.claude/skills/frontend-design/SKILL.md` | +| Scaffolding a new component | `.claude/skills/make-component/SKILL.md` | +| Full screen from design to controller-wired | `.claude/skills/design-first-workflow/SKILL.md` | + +## Anti-Patterns + +The following are hard blockers. The `component-visual-reviewer` will flag every one of them. + +| Anti-pattern | Correct approach | +|-------------|-----------------| +| `Color(0xFF...)` or `Colors.*` in component code | Use a semantic alias key from `DESIGN.md` | +| Hardcoded pixel values (`SizedBox(height: 13)`) | Wind spacing utilities on the 4px scale | +| A color token without its `dark:` counterpart | Every alias expands to a light+dark pair; no exceptions | +| Building a one-off widget when a library component exists | Check `docs/component-registry.md` first | +| Multiple preview classes in one `.preview.dart` file | One preview class per file | +| `Icons.*` inline in component body | Extract as `static const IconData _icon = Icons.x;` | +| CSS-only Wind utilities (`box-shadow`, `filter`, `transform`, `group-*`) | These are unsupported; use Flutter animation APIs instead | +| Hand-editing `lib/config/wind_theme.g.dart` | Run `dart run bin/dispatcher.dart design:sync` instead | +| Skipping the preview before shipping a component | Every component needs a preview; run `previews:refresh` | + +## Regeneration Commands + +| Need | Command | +|------|---------| +| Regenerate wind theme from DESIGN.md | `dart run bin/dispatcher.dart design:sync` | +| Regenerate preview catalog | `dart run bin/dispatcher.dart previews:refresh` | +| Lint DESIGN.md | `dart run bin/dispatcher.dart design:lint` | +| Scaffold component | `dart run bin/dispatcher.dart make:component ` | diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..7276eaf --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,66 @@ +# Magic Example + +Reference app for the `magic` framework and `magic_starter` starter kit. Single-brand violet, Wind semantic tokens, M3-role palette. Consumes and demonstrates the full design-first component system. + +## Stack + +- Flutter >=3.27.0, Dart >=3.6.0 +- `fluttersdk_magic` (framework: IoC, ORM, auth, routing via `go_router`) +- `magic_starter` (starter kit: auth, profile, teams, notifications, 13 opt-in features) +- `fluttersdk_wind` (utility-first styling: Wind className strings only) +- `magic_devtools` (dev-only preview catalog and dusk integration) + +## Design-First Rules + +These rules apply to every UI file. There are no exceptions. + +**1. Wind className and semantic tokens only.** +No `Color(0xFF...)`, no `Colors.*`, no hardcoded pixel values in component or view code. All color decisions go through semantic alias keys from `DESIGN.md` (e.g. `bg-surface`, `text-fg`, `border-color-border`). Every color alias expands to a light+dark pair; always include the `dark:` counterpart. + +**2. Build from the component library.** +Check `docs/component-registry.md` before writing any widget. If a component covers the need, use it. Only scaffold a new component when the registry has no match. + +**3. DESIGN.md is the theme source.** +`magic_example/DESIGN.md` is the single source of truth for colors, typography, spacing, and rounded values. The generated theme lives at `lib/config/wind_theme.g.dart` (do not hand-edit it). Regenerate after any DESIGN.md change with `dart run bin/dispatcher.dart design:sync`. + +**4. The `/preview` catalog is the visual feedback loop.** +Every new component requires a preview widget before it ships. Navigate to `/preview` in debug mode to see all registered previews in dark and light. Use dusk screenshots to verify token compliance before merging. + +## Commands + +All commands run via the dispatcher. Do not reference `magic_example:artisan` (it does not exist). + +```sh +# Scaffold a new component (chains previews:refresh automatically) +dart run bin/dispatcher.dart make:component [--variants=intent,size] [--slots] + +# Regenerate the wind theme from DESIGN.md +dart run bin/dispatcher.dart design:sync + +# Regenerate lib/preview/_previews.g.dart from *.preview.dart files +dart run bin/dispatcher.dart previews:refresh + +# Lint DESIGN.md token rules (13 checks) +dart run bin/dispatcher.dart design:lint + +# Navigate to /preview in the running app +./bin/fsa dusk:navigate --route=/preview + +# Screenshot the current screen (light or dark) +./bin/fsa dusk:screenshot -o .ac/evidence/-light.png + +# Analyze and format +flutter analyze +dart format . +flutter test +``` + +## Agent Infra + +- Skills: `.claude/skills/frontend-design/`, `.claude/skills/make-component/`, `.claude/skills/design-first-workflow/` +- Agents: `.claude/agents/component-visual-reviewer.md` +- Design culture docs: `docs/design-culture/` (apple-hig, material-design-3, refactoring-ui, accessibility-wcag, motion-interaction, wind-responsive) +- Component registry: `docs/component-registry.md` + +@DESIGN.md +@docs/design-culture/ diff --git a/DESIGN.md b/DESIGN.md new file mode 100644 index 0000000..a0db345 --- /dev/null +++ b/DESIGN.md @@ -0,0 +1,225 @@ +--- +name: Magic Example +description: > + Reference app for the magic framework, magic_starter, and the design-first + component system. Single-brand violet, Wind semantic tokens, M3-role palette. +colors: + surface: + light: "#FFFFFF" + dark: "#030712" + surface-container: + light: "#F9FAFB" + dark: "#111827" + surface-container-high: + light: "#F3F4F6" + dark: "#1F2937" + fg: + light: "#111827" + dark: "#F9FAFB" + fg-muted: + light: "#6B7280" + dark: "#9CA3AF" + fg-disabled: + light: "#D1D5DB" + dark: "#4B5563" + primary: + light: "#7C3AED" + dark: "#8B5CF6" + on-primary: + light: "#FFFFFF" + dark: "#FFFFFF" + primary-container: + light: "#EDE9FE" + dark: "#4C1D95" + accent: + light: "#4F46E5" + dark: "#6366F1" + border: + light: "#E5E7EB" + dark: "#374151" + border-subtle: + light: "#F3F4F6" + dark: "#1F2937" + destructive: + light: "#DC2626" + dark: "#EF4444" + on-destructive: + light: "#FFFFFF" + dark: "#FFFFFF" + destructive-container: + light: "#FEE2E2" + dark: "#7F1D1D" + success: + light: "#15803D" + dark: "#16A34A" + warning: + light: "#D97706" + dark: "#B45309" +typography: + display: + fontFamily: Inter + fontSize: 36px + fontWeight: "700" + lineHeight: 44px + letterSpacing: -0.02em + headline-lg: + fontFamily: Inter + fontSize: 28px + fontWeight: "700" + lineHeight: 36px + letterSpacing: -0.01em + headline-md: + fontFamily: Inter + fontSize: 22px + fontWeight: "600" + lineHeight: 30px + title-lg: + fontFamily: Inter + fontSize: 18px + fontWeight: "600" + lineHeight: 26px + body-lg: + fontFamily: Inter + fontSize: 16px + fontWeight: "400" + lineHeight: 26px + body-md: + fontFamily: Inter + fontSize: 14px + fontWeight: "400" + lineHeight: 22px + label-md: + fontFamily: Inter + fontSize: 14px + fontWeight: "600" + lineHeight: 20px + letterSpacing: 0.01em + label-sm: + fontFamily: Inter + fontSize: 12px + fontWeight: "500" + lineHeight: 16px +rounded: + sm: 4px + DEFAULT: 8px + md: 12px + lg: 16px + xl: 24px + full: 9999px +spacing: + xs: 4px + sm: 8px + md: 16px + lg: 24px + xl: 40px + 2xl: 64px + gutter: 16px + section: 32px +components: + button-primary: + backgroundColor: "{colors.primary}" + textColor: "{colors.on-primary}" + rounded: "{rounded.md}" + padding: "{spacing.md}" + button-destructive: + backgroundColor: "{colors.destructive}" + textColor: "{colors.on-destructive}" + rounded: "{rounded.md}" + padding: "{spacing.md}" + card-surface: + backgroundColor: "{colors.surface-container}" + rounded: "{rounded.lg}" + padding: "{spacing.lg}" + card-elevated: + backgroundColor: "{colors.surface}" + rounded: "{rounded.lg}" + padding: "{spacing.lg}" + input-field: + backgroundColor: "{colors.surface-container-high}" + rounded: "{rounded.DEFAULT}" + padding: "{spacing.md}" + badge-primary: + backgroundColor: "{colors.primary-container}" + rounded: "{rounded.full}" + padding: "{spacing.xs}" +--- + +## Overview + +Magic Example is the reference consumer app for the magic framework plus +magic_starter starter kit. Its design system is built around a single violet +brand with Material 3 role semantics, Wind utility tokens, and a mobile-first +responsive layout. + +The brand personality is precise, professional, and approachable. The violet +primary anchors interactive surfaces (buttons, active tabs, focus rings) while +gray neutrals keep the reading experience calm. The accent indigo provides a +distinct secondary signal without introducing a second brand color. + +For the responsive direction and accessible usage patterns, see +[docs/design-culture/](docs/design-culture/). + +## Colors + +The palette uses the M3 role model mapped onto 17 Wind semantic alias keys. A +single consumer-supplied `primary` MaterialColor drives shade resolution across +the component system; nothing else is hardcoded. + +Light mode background hierarchy: `surface` (white page) -> `surface-container` +(cards) -> `surface-container-high` (input backgrounds). Dark mode inverts +toward near-black gray steps. + +Primary violet (#7C3AED light, #8B5CF6 dark) provides a 5.7:1 contrast ratio +against white, passing WCAG AA for normal text. Destructive red (#DC2626) and +on-destructive white also pass at 4.8:1. + +See [docs/design-culture/accessibility-wcag.md](docs/design-culture/accessibility-wcag.md) +for contrast requirements and how `design:lint` enforces them. + +## Typography + +Inter is the app font: geometric, legible on small mobile screens, and neutral +enough not to compete with the violet brand. All sizes are in logical pixels +aligned to a 4px grid. + +For type hierarchy guidance see +[docs/design-culture/refactoring-ui.md](docs/design-culture/refactoring-ui.md). + +## Layout + +The app uses a mobile-first 1-column layout that expands to a sidebar + content +column at the `md` breakpoint (768px). Spacing follows the 4px logical scale +defined in the `spacing` section above; `gutter` (16px) is the horizontal +content margin on narrow screens and `section` (32px) separates stacked +sections. + +For responsive layout patterns and breakpoint usage, see +[docs/design-culture/wind-responsive.md](docs/design-culture/wind-responsive.md). + +## Elevation & Depth + +Surface hierarchy is expressed through tonal background shifts, not drop +shadows. `surface-container` sits one level above `surface`; `surface-container-high` +is used for input backgrounds and nested panels. + +Subtle border lines (`border-color-border`) separate sections instead of +shadows, keeping the UI light and reducing visual noise. + +## Shapes + +Corner radii follow the 4px logical scale: + +- Inputs and small controls: `DEFAULT` (8px) for a modern, structured look. +- Cards and dialogs: `lg` (16px) to feel contained and distinct. +- Badges and chips: `full` (9999px) for a pill shape. +- Buttons: `md` (12px), balancing substance and friendliness. + +## Components + +Components are described in detail in +[docs/design-culture/material-design-3.md](docs/design-culture/material-design-3.md). + +Variant matrices for every component are available via `flutter run` -> +`/preview` (debug builds only). Run `dart run bin/dispatcher.dart design:lint` +to validate token usage; run `dart run bin/dispatcher.dart design:sync` to +regenerate the Wind theme from this file. diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..0d29021 --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,28 @@ +# 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 + +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..b18d0e2 --- /dev/null +++ b/android/app/build.gradle.kts @@ -0,0 +1,45 @@ +plugins { + id("com.android.application") + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. + id("dev.flutter.flutter-gradle-plugin") +} + +android { + namespace = "com.fluttersdk.magic_example" + compileSdk = flutter.compileSdkVersion + ndkVersion = flutter.ndkVersion + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId = "com.fluttersdk.magic_example" + // 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") + } + } +} + +kotlin { + compilerOptions { + jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17 + } +} + +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..b21998b --- /dev/null +++ b/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/kotlin/com/fluttersdk/magic_example/MainActivity.kt b/android/app/src/main/kotlin/com/fluttersdk/magic_example/MainActivity.kt new file mode 100644 index 0000000..1955909 --- /dev/null +++ b/android/app/src/main/kotlin/com/fluttersdk/magic_example/MainActivity.kt @@ -0,0 +1,5 @@ +package com.fluttersdk.magic_example + +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..e96108c --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,6 @@ +org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError +android.useAndroidX=true +# This newDsl flag was added by the Flutter template +android.newDsl=false +# This builtInKotlin flag was added by the Flutter template +android.builtInKotlin=false diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..2d428bf --- /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-9.1.0-all.zip diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts new file mode 100644 index 0000000..c21f0c5 --- /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 "9.0.1" apply false + id("org.jetbrains.kotlin.android") version "2.3.20" apply false +} + +include(":app") diff --git a/assets/lang/en.json b/assets/lang/en.json new file mode 100644 index 0000000..77fc176 --- /dev/null +++ b/assets/lang/en.json @@ -0,0 +1,290 @@ +{ + "common": { + "welcome": "Welcome to ", + "loading": "Loading...", + "save": "Save", + "cancel": "Cancel", + "confirm": "Confirm", + "delete": "Delete", + "edit": "Edit", + "go_to_dashboard": "Go to Dashboard", + "remove": "Remove", + "toggle_theme": "Toggle theme", + "unknown": "Unknown", + "upload": "Upload", + "user": "User", + "done": "Done", + "error_occurred": "An unexpected error occurred. Please try again.", + "page_of": ":current / :total" + }, + "validation": { + "required": "The :attribute field is required.", + "email": "The :attribute must be a valid email address.", + "min": "The :attribute must be at least :min characters.", + "max": "The :attribute may not be greater than :max characters.", + "confirmed": "The :attribute confirmation does not match.", + "accepted": "The :attribute must be accepted.", + "same": "The :attribute and :other must match." + }, + "app": { + "name": "My App" + }, + "attributes": { + "current_password": "Current Password", + "email": "Email Address", + "name": "Name", + "new_password": "New Password", + "password": "Password", + "password_confirmation": "Confirm Password", + "phone": "Phone Number", + "phone_country": "Country Code", + "role": "Role", + "timezone": "Timezone" + }, + "auth": { + "already_have_account": "Already have an account?", + "authentication_code": "Authentication Code", + "back_to_login": "Back to Sign In", + "challenge_failed": "Two-factor challenge failed. Please try again.", + "dont_have_account": "Don't have an account?", + "forgot_password": "Forgot password?", + "forgot_password_subtitle": "Enter your email to receive a reset link", + "forgot_password_title": "Forgot Password", + "invalid_response": "An unexpected error occurred. Please try again.", + "login_failed": "Login failed. Please check your credentials.", + "login_subtitle": "Sign in to your account", + "login_title": "Sign In", + "logout": "Logout", + "or_continue_with": "Or continue with", + "password_reset_failed": "Unable to reset password. Please try again.", + "password_reset_success": "Your password has been reset. You can now sign in.", + "profile": "Profile", + "recovery_code": "Recovery Code", + "register_failed": "Registration failed. Please try again.", + "register_subtitle": "Create your account to get started", + "register_title": "Create Account", + "remember_me": "Remember me", + "reset_link_failed": "Unable to send reset link. Please try again.", + "reset_link_sent": "A password reset link has been sent to your email.", + "reset_password_button": "Reset Password", + "reset_password_subtitle": "Enter your new password", + "reset_password_title": "Reset Password", + "send_reset_link": "Send Reset Link", + "sign_in": "Sign In", + "sign_in_with": "Sign in with :provider", + "sign_up": "Sign up", + "sign_up_with": "Sign up with :provider", + "signed_in_as": "Signed in as", + "two_factor_challenge": "Two-Factor Challenge", + "two_factor_code_description": "Please confirm access to your account by entering the authentication code from your authenticator application.", + "two_factor_recovery_description": "Please confirm access to your account by entering one of your emergency recovery codes.", + "use_authentication_code": "Use an authentication code", + "use_recovery_code": "Use a recovery code", + "verify": "Verify", + "agree_to_legal": "By creating an account, you agree to our", + "terms_of_service": "Terms of Service", + "privacy_policy": "Privacy Policy", + "legal_and": "and" + }, + "errors": { + "unexpected": "An unexpected error occurred. Please try again.", + "network_error": "Network connection failed. Please check your internet connection and try again.", + "network_timeout": "Request timed out. Please try again later." + }, + "fields": { + "email_placeholder": "you@example.com", + "name_placeholder": "Enter your full name", + "password_confirmation_placeholder": "Repeat your password", + "password_placeholder": "At least 8 characters", + "phone_country_placeholder": "Select country code", + "phone_placeholder": "+905301234567", + "otp_placeholder": "123456" + }, + "magic_starter": { + "auth": { + "continue_as_guest": "Continue as Guest", + "guest_login_error": "Unable to continue as guest. Please try again." + }, + "email_verification": { + "resend_button": "Resend Verification Email", + "section_title": "Email Verification", + "send_error": "Failed to send verification email. Please try again.", + "sent": "Verification email sent successfully.", + "unverified_description": "Your email address is not verified yet. Please verify it to secure your account.", + "unverified_title": "Email address not verified", + "verified": "Your email address is verified." + }, + "guest_upgrade": { + "button": "Upgrade Account", + "description": "Create a permanent account to keep your data and unlock all features.", + "title": "Upgrade Your Guest Account" + }, + "notifications": { + "fetch_error": "Failed to load notification preferences." + }, + "newsletter": { + "fetch_error": "Failed to load newsletter preferences.", + "section_description": "Receive product updates and announcements by email.", + "section_title": "Newsletter Preferences", + "subscribe_label": "Subscribe to product updates and news", + "subscribed_status": "You are currently subscribed.", + "toggle_button": "Save Preferences", + "toggle_label": "Receive Newsletter", + "unsubscribed_status": "You are not subscribed.", + "update_error": "Failed to update newsletter preferences." + }, + "otp": { + "back_button": "Back", + "code_label": "Verification Code", + "code_subtitle": "Enter the verification code sent to your phone number.", + "code_title": "Verify Phone Number", + "phone_subtitle": "Enter your phone number to receive a verification code.", + "phone_title": "Phone Verification", + "resend_link": "Resend code", + "send_code_button": "Send Code", + "send_error": "Failed to send verification code. Please try again.", + "verify_button": "Verify Code", + "verify_error": "Failed to verify code. Please try again." + }, + "profile": { + "delete_account": { + "button": "Delete Account", + "description": "Permanently delete your account and all associated data. This action cannot be undone.", + "guest_upgrade_description": "Convert your guest account to a full account to manage or delete your data.", + "guest_upgrade_title": "Account Upgrade Required", + "password_label": "Confirm your password", + "title": "Delete Account" + } + } + }, + "nav": { + "dashboard": "Dashboard", + "profile": "Profile", + "settings": "Settings", + "system": "System" + }, + "notifications": { + "empty": "No notifications", + "list_subtitle": "View and manage your notifications", + "load_failed": "Failed to load notifications", + "mark_all_read": "Mark all as read", + "preferences_description": "Manage how and when you receive notifications", + "preferences_title": "Notification Preferences", + "settings": "Notification Settings", + "title": "Notifications", + "view_all": "View all notifications", + "badge_overflow": "9+", + "no_preferences": "No notification preferences available.", + "channel_email": "Email", + "channel_in_app": "In-App", + "channel_push": "Push" + }, + "profile": { + "browser_sessions": "Browser Sessions", + "browser_sessions_description": "Manage and sign out your active sessions on other browsers and devices.", + "confirm_password": "Confirm Password", + "confirm_password_description": "Please confirm your password before continuing.", + "copy_recovery_codes": "Copy Recovery Codes", + "current_device": "Current device", + "delete_failed": "Failed to delete account. Please try again.", + "extended_information": "Extended Information", + "language_label": "Language", + "logout_other_sessions": "Log Out Other Browser Sessions", + "no_active_sessions": "No active sessions found.", + "other_sessions_revoke_error": "Failed to revoke other browser sessions.", + "password_update_failed": "Failed to update password. Please check your current password.", + "password_updated": "Password updated successfully.", + "phone_country_label": "Country Code", + "phone_label": "Phone Number", + "photo_delete_failed": "Failed to remove profile photo.", + "photo_deleted": "Profile photo removed.", + "photo_requirements": "JPG, GIF or PNG. Max size of 1MB.", + "photo_update_failed": "Failed to update profile photo.", + "photo_updated": "Profile photo updated successfully.", + "profile_information": "Profile Information", + "profile_photo": "Profile Photo", + "revoke": "Revoke", + "session_revoke_error": "Failed to revoke browser session.", + "sessions_fetch_error": "Failed to load browser sessions.", + "settings": "Profile Settings", + "settings_subtitle": "Manage your account information and preferences", + "timezone_label": "Timezone", + "timezone_search": "Search timezone", + "timezone_select": "Select timezone", + "two_factor_authentication": "Two-Factor Authentication", + "two_factor_code_label": "Authentication Code", + "two_factor_code_placeholder": "Enter the 6-digit code", + "two_factor_confirm": "Confirm", + "two_factor_confirm_failed": "Failed to confirm two-factor authentication.", + "two_factor_disable": "Disable", + "two_factor_disable_failed": "Failed to disable two-factor authentication.", + "two_factor_disabled_description": "Two-factor authentication is currently disabled for your account.", + "two_factor_enable": "Enable", + "two_factor_enable_failed": "Failed to enable two-factor authentication.", + "two_factor_enabled": "Two-factor authentication is enabled.", + "two_factor_enabled_description": "Your account is protected with two-factor authentication.", + "two_factor_manual_entry": "Manual entry key", + "two_factor_recovery_codes_description": "Store these recovery codes in a secure location. They can be used to access your account if you lose your authenticator device.", + "two_factor_recovery_codes_fetch_failed": "Failed to load recovery codes.", + "two_factor_recovery_codes_regenerate_failed": "Failed to regenerate recovery codes.", + "two_factor_regenerate_codes": "Regenerate Recovery Codes", + "two_factor_setup_description": "Scan the QR code using your authenticator app and enter the generated code to confirm setup.", + "two_factor_show_recovery_codes": "Show Recovery Codes", + "update_failed": "Failed to update profile. Please try again.", + "update_password": "Update Password", + "updated": "Profile updated successfully.", + "copy_recovery_codes_success": "Recovery codes copied to clipboard.", + "two_factor_auth": "Two-Factor Authentication", + "two_factor": { + "copy_codes": "Copy All Codes", + "invalid_code": "The provided two-factor authentication code was invalid." + } + }, + "teams": { + "accept_invitation": "Accept Invitation", + "accept_invitation_subtitle": "Join a team by accepting this invitation", + "accept_invite_failed": "Failed to accept invitation.", + "cancel_invite_failed": "Failed to cancel invitation.", + "cancel_invite_label": "Cancel Invitation", + "confirm_cancel_invite": "Are you sure you want to cancel this invitation?", + "confirm_remove_member": "Are you sure you want to remove this member from the team?", + "create_failed": "Failed to create team.", + "create_team": "Create New Team", + "create_team_subtitle": "Create a new team to collaborate with others", + "created": "Team created successfully.", + "current_members": "Current Members", + "feature_disabled": "Team features are not enabled for this application.", + "general_settings": "General", + "invite_accepted": "You have joined the team successfully.", + "invite_canceled": "Invitation canceled successfully.", + "invite_failed": "Failed to send invitation.", + "invite_member": "Invite Member", + "invite_sent": "Invitation sent successfully.", + "member_remove_failed": "Failed to remove member.", + "member_removed": "Member removed from team", + "no_invitations": "No pending invitations", + "no_members": "No team members yet", + "no_team_selected": "No team selected.", + "pending": "Pending", + "pending_invitations": "Pending Invitations", + "remove_member_label": "Remove Member", + "role_admin": "Admin", + "role_member": "Member", + "select_team": "Select a team", + "send_invite": "Send Invite", + "settings": "Team Settings", + "settings_subtitle": "Manage your team name, members, and invitations", + "switch_failed": "Failed to switch team.", + "team": "Team", + "team_name": "Team Name", + "update_failed": "Failed to update team.", + "updated": "Team updated successfully." + }, + "time": { + "days_ago": ":days d ago", + "hours_ago": ":hours h ago", + "just_now": "Just now", + "minutes_ago": ":minutes m ago", + "date_format": ":day/:month/:year" + } +} \ No newline at end of file diff --git a/backend/.editorconfig b/backend/.editorconfig new file mode 100644 index 0000000..6df8428 --- /dev/null +++ b/backend/.editorconfig @@ -0,0 +1,18 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.{yml,yaml}] +indent_size = 2 + +[{compose,docker-compose}.{yml,yaml}] +indent_size = 4 diff --git a/backend/.env.example b/backend/.env.example new file mode 100644 index 0000000..c0660ea --- /dev/null +++ b/backend/.env.example @@ -0,0 +1,65 @@ +APP_NAME=Laravel +APP_ENV=local +APP_KEY= +APP_DEBUG=true +APP_URL=http://localhost + +APP_LOCALE=en +APP_FALLBACK_LOCALE=en +APP_FAKER_LOCALE=en_US + +APP_MAINTENANCE_DRIVER=file +# APP_MAINTENANCE_STORE=database + +# PHP_CLI_SERVER_WORKERS=4 + +BCRYPT_ROUNDS=12 + +LOG_CHANNEL=stack +LOG_STACK=single +LOG_DEPRECATIONS_CHANNEL=null +LOG_LEVEL=debug + +DB_CONNECTION=sqlite +# DB_HOST=127.0.0.1 +# DB_PORT=3306 +# DB_DATABASE=laravel +# DB_USERNAME=root +# DB_PASSWORD= + +SESSION_DRIVER=database +SESSION_LIFETIME=120 +SESSION_ENCRYPT=false +SESSION_PATH=/ +SESSION_DOMAIN=null + +BROADCAST_CONNECTION=log +FILESYSTEM_DISK=local +QUEUE_CONNECTION=database + +CACHE_STORE=database +# CACHE_PREFIX= + +MEMCACHED_HOST=127.0.0.1 + +REDIS_CLIENT=phpredis +REDIS_HOST=127.0.0.1 +REDIS_PASSWORD=null +REDIS_PORT=6379 + +MAIL_MAILER=log +MAIL_SCHEME=null +MAIL_HOST=127.0.0.1 +MAIL_PORT=2525 +MAIL_USERNAME=null +MAIL_PASSWORD=null +MAIL_FROM_ADDRESS="hello@example.com" +MAIL_FROM_NAME="${APP_NAME}" + +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_DEFAULT_REGION=us-east-1 +AWS_BUCKET= +AWS_USE_PATH_STYLE_ENDPOINT=false + +VITE_APP_NAME="${APP_NAME}" diff --git a/backend/.gitattributes b/backend/.gitattributes new file mode 100644 index 0000000..fcb21d3 --- /dev/null +++ b/backend/.gitattributes @@ -0,0 +1,11 @@ +* text=auto eol=lf + +*.blade.php diff=html +*.css diff=css +*.html diff=html +*.md diff=markdown +*.php diff=php + +/.github export-ignore +CHANGELOG.md export-ignore +.styleci.yml export-ignore diff --git a/backend/.gitignore b/backend/.gitignore new file mode 100644 index 0000000..7be55e2 --- /dev/null +++ b/backend/.gitignore @@ -0,0 +1,27 @@ +*.log +.DS_Store +.env +.env.backup +.env.production +.phpactor.json +.phpunit.result.cache +/.codex +/.cursor/ +/.idea +/.nova +/.phpunit.cache +/.vscode +/.zed +/auth.json +/node_modules +/public/build +/public/fonts-manifest.dev.json +/public/hot +/public/storage +/storage/*.key +/storage/pail +/vendor +_ide_helper.php +Homestead.json +Homestead.yaml +Thumbs.db diff --git a/backend/.npmrc b/backend/.npmrc new file mode 100644 index 0000000..495a6af --- /dev/null +++ b/backend/.npmrc @@ -0,0 +1,2 @@ +ignore-scripts=true +audit=true diff --git a/backend/README.md b/backend/README.md new file mode 100644 index 0000000..5ad1377 --- /dev/null +++ b/backend/README.md @@ -0,0 +1,58 @@ +

Laravel Logo

+ +

+Build Status +Total Downloads +Latest Stable Version +License +

+ +## About Laravel + +Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as: + +- [Simple, fast routing engine](https://laravel.com/docs/routing). +- [Powerful dependency injection container](https://laravel.com/docs/container). +- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage. +- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent). +- Database agnostic [schema migrations](https://laravel.com/docs/migrations). +- [Robust background job processing](https://laravel.com/docs/queues). +- [Real-time event broadcasting](https://laravel.com/docs/broadcasting). + +Laravel is accessible, powerful, and provides tools required for large, robust applications. + +## Learning Laravel + +Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework. + +In addition, [Laracasts](https://laracasts.com) contains thousands of video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library. + +You can also watch bite-sized lessons with real-world projects on [Laravel Learn](https://laravel.com/learn), where you will be guided through building a Laravel application from scratch while learning PHP fundamentals. + +## Agentic Development + +Laravel's predictable structure and conventions make it ideal for AI coding agents like Claude Code, Cursor, and GitHub Copilot. Install [Laravel Boost](https://laravel.com/docs/ai) to supercharge your AI workflow: + +```bash +composer require laravel/boost --dev + +php artisan boost:install +``` + +Boost provides your agent 15+ tools and skills that help agents build Laravel applications while following best practices. + +## Contributing + +Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions). + +## Code of Conduct + +In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct). + +## Security Vulnerabilities + +If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed. + +## License + +The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT). diff --git a/backend/app/Actions/MagicStarter/AddTeamMember.php b/backend/app/Actions/MagicStarter/AddTeamMember.php new file mode 100644 index 0000000..22fdd3a --- /dev/null +++ b/backend/app/Actions/MagicStarter/AddTeamMember.php @@ -0,0 +1,28 @@ + $input The validated team data. + * @return Model The created team instance. + */ + public function create(Authenticatable $user, array $input): Model + { + // TODO: Implement team creation logic. + throw new RuntimeException('CreateTeam action not implemented. Publish and implement this stub.'); + } +} diff --git a/backend/app/Actions/MagicStarter/CreateUser.php b/backend/app/Actions/MagicStarter/CreateUser.php new file mode 100644 index 0000000..91672c3 --- /dev/null +++ b/backend/app/Actions/MagicStarter/CreateUser.php @@ -0,0 +1,25 @@ + $input The validated registration data. + * @return Authenticatable The created user instance. + */ + public function create(array $input): Authenticatable + { + // TODO: Implement user creation logic. + // Example: Create user model, hash password, create personal team. + throw new \RuntimeException('CreateUser action not implemented. Publish and implement this stub.'); + } +} diff --git a/backend/app/Actions/MagicStarter/DeleteTeam.php b/backend/app/Actions/MagicStarter/DeleteTeam.php new file mode 100644 index 0000000..a1b2805 --- /dev/null +++ b/backend/app/Actions/MagicStarter/DeleteTeam.php @@ -0,0 +1,24 @@ + $input The validated update data. + */ + public function update(Authenticatable $user, Model $team, array $input): void + { + // TODO: Implement team update logic. + // Example: authorize, update model attributes. + throw new \RuntimeException('UpdateTeam action not implemented. Publish and implement this stub.'); + } +} diff --git a/backend/app/Actions/MagicStarter/UpdateTeamMemberRole.php b/backend/app/Actions/MagicStarter/UpdateTeamMemberRole.php new file mode 100644 index 0000000..eed8611 --- /dev/null +++ b/backend/app/Actions/MagicStarter/UpdateTeamMemberRole.php @@ -0,0 +1,28 @@ + $input The validated password data. + */ + public function update(Authenticatable $user, array $input): void + { + // TODO: Implement password update logic. + // Example: check current_password, hash new password, save. + throw new \RuntimeException('UpdateUserPassword action not implemented. Publish and implement this stub.'); + } +} diff --git a/backend/app/Actions/MagicStarter/UpdateUserProfile.php b/backend/app/Actions/MagicStarter/UpdateUserProfile.php new file mode 100644 index 0000000..dc0a503 --- /dev/null +++ b/backend/app/Actions/MagicStarter/UpdateUserProfile.php @@ -0,0 +1,25 @@ + $input The validated profile data. + */ + public function update(Authenticatable $user, array $input): void + { + // TODO: Implement profile update logic. + // Example: handle unique email check, update name/email, save. + throw new \RuntimeException('UpdateUserProfile action not implemented. Publish and implement this stub.'); + } +} diff --git a/backend/app/Http/Controllers/Controller.php b/backend/app/Http/Controllers/Controller.php new file mode 100644 index 0000000..8677cd5 --- /dev/null +++ b/backend/app/Http/Controllers/Controller.php @@ -0,0 +1,8 @@ + + */ + protected $fillable = [ + 'user_id', + 'name', + 'personal_team', + 'profile_photo_path', + ]; +} diff --git a/backend/app/Models/TeamInvitation.php b/backend/app/Models/TeamInvitation.php new file mode 100644 index 0000000..f83a8f4 --- /dev/null +++ b/backend/app/Models/TeamInvitation.php @@ -0,0 +1,11 @@ + + */ + protected $fillable = [ + 'name', + 'email', + 'password', + 'phone', + 'phone_country', + 'locale', + 'timezone', + ]; + + /** + * The attributes that should be hidden for serialization. + * + * @var array + */ + protected $hidden = [ + 'password', + 'remember_token', + 'two_factor_secret', + 'two_factor_recovery_codes', + ]; + + /** + * Get the attributes that should be cast. + * + * @return array + */ + protected function casts(): array + { + return [ + 'email_verified_at' => 'datetime', + 'password' => 'hashed', + 'is_guest' => 'boolean', + ]; + } +} diff --git a/backend/app/Policies/TeamPolicy.php b/backend/app/Policies/TeamPolicy.php new file mode 100644 index 0000000..0e26508 --- /dev/null +++ b/backend/app/Policies/TeamPolicy.php @@ -0,0 +1,62 @@ +belongsToTeam($team); + } + + /** + * Determine whether the user can update the team. + */ + public function update(mixed $user, mixed $team): bool + { + return $user->ownsTeam($team); + } + + /** + * Determine whether the user can delete the team. + */ + public function delete(mixed $user, mixed $team): bool + { + return $user->ownsTeam($team); + } + + /** + * Determine whether the user can manage team members. + */ + public function manageMembers(mixed $user, mixed $team): bool + { + return $user->ownsTeam($team) || $user->hasTeamRole($team, 'admin'); + } + + /** + * Determine whether the user can manage team invitations. + */ + public function manageInvitations(mixed $user, mixed $team): bool + { + return $user->ownsTeam($team) || $user->hasTeamRole($team, 'admin'); + } + + /** + * Determine whether the user can switch to the team. + */ + public function switchTo(mixed $user, mixed $team): bool + { + return $user->belongsToTeam($team); + } +} diff --git a/backend/app/Providers/AppServiceProvider.php b/backend/app/Providers/AppServiceProvider.php new file mode 100644 index 0000000..452e6b6 --- /dev/null +++ b/backend/app/Providers/AppServiceProvider.php @@ -0,0 +1,24 @@ +handleCommand(new ArgvInput); + +exit($status); diff --git a/backend/bootstrap/app.php b/backend/bootstrap/app.php new file mode 100644 index 0000000..7a2848f --- /dev/null +++ b/backend/bootstrap/app.php @@ -0,0 +1,21 @@ +withRouting( + web: __DIR__.'/../routes/web.php', + commands: __DIR__.'/../routes/console.php', + health: '/up', + ) + ->withMiddleware(function (Middleware $middleware): void { + // + }) + ->withExceptions(function (Exceptions $exceptions): void { + $exceptions->shouldRenderJsonWhen( + fn (Request $request) => $request->is('api/*'), + ); + })->create(); diff --git a/backend/bootstrap/cache/.gitignore b/backend/bootstrap/cache/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/backend/bootstrap/cache/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/backend/bootstrap/providers.php b/backend/bootstrap/providers.php new file mode 100644 index 0000000..fc94ae6 --- /dev/null +++ b/backend/bootstrap/providers.php @@ -0,0 +1,7 @@ +=5.0.0" + }, + "require-dev": { + "doctrine/dbal": "^4.0.0", + "nesbot/carbon": "^2.71.0 || ^3.0.0", + "phpunit/phpunit": "^10.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Carbon\\Doctrine\\": "src/Carbon/Doctrine/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "KyleKatarn", + "email": "kylekatarnls@gmail.com" + } + ], + "description": "Types to use Carbon in Doctrine", + "keywords": [ + "carbon", + "date", + "datetime", + "doctrine", + "time" + ], + "support": { + "issues": "https://github.com/CarbonPHP/carbon-doctrine-types/issues", + "source": "https://github.com/CarbonPHP/carbon-doctrine-types/tree/3.2.0" + }, + "funding": [ + { + "url": "https://github.com/kylekatarnls", + "type": "github" + }, + { + "url": "https://opencollective.com/Carbon", + "type": "open_collective" + }, + { + "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon", + "type": "tidelift" + } + ], + "time": "2024-02-09T16:56:22+00:00" + }, + { + "name": "dasprid/enum", + "version": "1.0.7", + "source": { + "type": "git", + "url": "https://github.com/DASPRiD/Enum.git", + "reference": "b5874fa9ed0043116c72162ec7f4fb50e02e7cce" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/b5874fa9ed0043116c72162ec7f4fb50e02e7cce", + "reference": "b5874fa9ed0043116c72162ec7f4fb50e02e7cce", + "shasum": "" + }, + "require": { + "php": ">=7.1 <9.0" + }, + "require-dev": { + "phpunit/phpunit": "^7 || ^8 || ^9 || ^10 || ^11", + "squizlabs/php_codesniffer": "*" + }, + "type": "library", + "autoload": { + "psr-4": { + "DASPRiD\\Enum\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Ben Scholzen 'DASPRiD'", + "email": "mail@dasprids.de", + "homepage": "https://dasprids.de/", + "role": "Developer" + } + ], + "description": "PHP 7.1 enum implementation", + "keywords": [ + "enum", + "map" + ], + "support": { + "issues": "https://github.com/DASPRiD/Enum/issues", + "source": "https://github.com/DASPRiD/Enum/tree/1.0.7" + }, + "time": "2025-09-16T12:23:56+00:00" + }, + { + "name": "dflydev/dot-access-data", + "version": "v3.0.3", + "source": { + "type": "git", + "url": "https://github.com/dflydev/dflydev-dot-access-data.git", + "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/a23a2bf4f31d3518f3ecb38660c95715dfead60f", + "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^0.12.42", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.3", + "scrutinizer/ocular": "1.6.0", + "squizlabs/php_codesniffer": "^3.5", + "vimeo/psalm": "^4.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Dflydev\\DotAccessData\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dragonfly Development Inc.", + "email": "info@dflydev.com", + "homepage": "http://dflydev.com" + }, + { + "name": "Beau Simensen", + "email": "beau@dflydev.com", + "homepage": "http://beausimensen.com" + }, + { + "name": "Carlos Frutos", + "email": "carlos@kiwing.it", + "homepage": "https://github.com/cfrutos" + }, + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com" + } + ], + "description": "Given a deep data structure, access data by dot notation.", + "homepage": "https://github.com/dflydev/dflydev-dot-access-data", + "keywords": [ + "access", + "data", + "dot", + "notation" + ], + "support": { + "issues": "https://github.com/dflydev/dflydev-dot-access-data/issues", + "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.3" + }, + "time": "2024-07-08T12:26:09+00:00" + }, + { + "name": "doctrine/inflector", + "version": "2.1.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/inflector.git", + "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/6d6c96277ea252fc1304627204c3d5e6e15faa3b", + "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^12.0 || ^13.0", + "phpstan/phpstan": "^1.12 || ^2.0", + "phpstan/phpstan-phpunit": "^1.4 || ^2.0", + "phpstan/phpstan-strict-rules": "^1.6 || ^2.0", + "phpunit/phpunit": "^8.5 || ^12.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Inflector\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.", + "homepage": "https://www.doctrine-project.org/projects/inflector.html", + "keywords": [ + "inflection", + "inflector", + "lowercase", + "manipulation", + "php", + "plural", + "singular", + "strings", + "uppercase", + "words" + ], + "support": { + "issues": "https://github.com/doctrine/inflector/issues", + "source": "https://github.com/doctrine/inflector/tree/2.1.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector", + "type": "tidelift" + } + ], + "time": "2025-08-10T19:31:58+00:00" + }, + { + "name": "doctrine/lexer", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "doctrine/coding-standard": "^12", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.5", + "psalm/plugin-phpunit": "^0.18.3", + "vimeo/psalm": "^5.21" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Lexer\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "https://www.doctrine-project.org/projects/lexer.html", + "keywords": [ + "annotations", + "docblock", + "lexer", + "parser", + "php" + ], + "support": { + "issues": "https://github.com/doctrine/lexer/issues", + "source": "https://github.com/doctrine/lexer/tree/3.0.1" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", + "type": "tidelift" + } + ], + "time": "2024-02-05T11:56:58+00:00" + }, + { + "name": "dragonmantank/cron-expression", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/dragonmantank/cron-expression.git", + "reference": "d61a8a9604ec1f8c3d150d09db6ce98b32675013" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/d61a8a9604ec1f8c3d150d09db6ce98b32675013", + "reference": "d61a8a9604ec1f8c3d150d09db6ce98b32675013", + "shasum": "" + }, + "require": { + "php": "^8.2|^8.3|^8.4|^8.5" + }, + "replace": { + "mtdowling/cron-expression": "^1.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.32|^2.1.31", + "phpunit/phpunit": "^8.5.48|^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Cron\\": "src/Cron/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Tankersley", + "email": "chris@ctankersley.com", + "homepage": "https://github.com/dragonmantank" + } + ], + "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due", + "keywords": [ + "cron", + "schedule" + ], + "support": { + "issues": "https://github.com/dragonmantank/cron-expression/issues", + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.6.0" + }, + "funding": [ + { + "url": "https://github.com/dragonmantank", + "type": "github" + } + ], + "time": "2025-10-31T18:51:33+00:00" + }, + { + "name": "egulias/email-validator", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/egulias/EmailValidator.git", + "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa", + "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa", + "shasum": "" + }, + "require": { + "doctrine/lexer": "^2.0 || ^3.0", + "php": ">=8.1", + "symfony/polyfill-intl-idn": "^1.26" + }, + "require-dev": { + "phpunit/phpunit": "^10.2", + "vimeo/psalm": "^5.12" + }, + "suggest": { + "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Egulias\\EmailValidator\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eduardo Gulias Davis" + } + ], + "description": "A library for validating emails against several RFCs", + "homepage": "https://github.com/egulias/EmailValidator", + "keywords": [ + "email", + "emailvalidation", + "emailvalidator", + "validation", + "validator" + ], + "support": { + "issues": "https://github.com/egulias/EmailValidator/issues", + "source": "https://github.com/egulias/EmailValidator/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/egulias", + "type": "github" + } + ], + "time": "2025-03-06T22:45:56+00:00" + }, + { + "name": "firebase/php-jwt", + "version": "v7.1.0", + "source": { + "type": "git", + "url": "https://github.com/googleapis/php-jwt.git", + "reference": "b374a5d1a4f1f67fadc2165cdb284645945e2fc0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/googleapis/php-jwt/zipball/b374a5d1a4f1f67fadc2165cdb284645945e2fc0", + "reference": "b374a5d1a4f1f67fadc2165cdb284645945e2fc0", + "shasum": "" + }, + "require": { + "php": "^8.0" + }, + "require-dev": { + "guzzlehttp/guzzle": "^7.4", + "phpfastcache/phpfastcache": "^9.2", + "phpseclib/phpseclib": "~3.0", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", + "psr/cache": "^2.0||^3.0", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0" + }, + "suggest": { + "ext-sodium": "Support EdDSA (Ed25519) signatures", + "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present", + "phpseclib/phpseclib": "Support PS256 (RSASSA-PSS) signatures" + }, + "type": "library", + "autoload": { + "psr-4": { + "Firebase\\JWT\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Neuman Vong", + "email": "neuman+pear@twilio.com", + "role": "Developer" + }, + { + "name": "Anant Narayanan", + "email": "anant@php.net", + "role": "Developer" + } + ], + "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.", + "homepage": "https://github.com/googleapis/php-jwt", + "keywords": [ + "jwt", + "php" + ], + "support": { + "issues": "https://github.com/googleapis/php-jwt/issues", + "source": "https://github.com/googleapis/php-jwt/tree/v7.1.0" + }, + "time": "2026-06-11T17:54:14+00:00" + }, + { + "name": "fluttersdk/magic-starter-laravel", + "version": "dev-master", + "dist": { + "type": "path", + "url": "../../magic-starter-laravel", + "reference": "c569489d9b86d81bca018d758dc21f46f52bc93a" + }, + "require": { + "bacon/bacon-qr-code": "^3.0", + "illuminate/database": "^12.0|^13.0", + "illuminate/routing": "^12.0|^13.0", + "illuminate/support": "^12.0|^13.0", + "laravel/sanctum": "^4.0", + "laravel/socialite": "^5.0", + "onesignal/onesignal-php-api": "^5.3", + "php": "^8.2", + "pragmarx/google2fa": "^8.0|^9.0" + }, + "require-dev": { + "larastan/larastan": "^3.0", + "laravel/pint": "^1.27", + "orchestra/testbench": "^10.0", + "phpunit/phpunit": "^11.0" + }, + "suggest": { + "geoip2/geoip2": "Optional: for IP geolocation in session management" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "FlutterSdk\\MagicStarter\\MagicStarterServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "FlutterSdk\\MagicStarter\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "FlutterSdk\\MagicStarter\\Tests\\": "tests/", + "FlutterSdk\\MagicStarter\\Database\\Factories\\": "database/factories/" + } + }, + "scripts": { + "test": [ + "vendor/bin/phpunit" + ], + "lint": [ + "vendor/bin/pint --test" + ], + "lint:fix": [ + "vendor/bin/pint" + ], + "analyse": [ + "vendor/bin/phpstan analyse --memory-limit=1G" + ] + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "FlutterSdk", + "email": "hello@fluttersdk.com" + } + ], + "description": "Magic Framework Laravel backend starter package.", + "homepage": "https://magic.fluttersdk.com/starter", + "keywords": [ + "auth", + "jetstream", + "laravel", + "profile", + "sanctum", + "starter", + "teams" + ], + "support": { + "docs": "https://magic.fluttersdk.com/packages/starter-laravel/getting-started/installation", + "issues": "https://github.com/fluttersdk/magic-starter-laravel/issues", + "source": "https://github.com/fluttersdk/magic-starter-laravel" + }, + "transport-options": { + "symlink": true, + "relative": true + } + }, + { + "name": "fruitcake/php-cors", + "version": "v1.4.0", + "source": { + "type": "git", + "url": "https://github.com/fruitcake/php-cors.git", + "reference": "38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379", + "reference": "38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379", + "shasum": "" + }, + "require": { + "php": "^8.1", + "symfony/http-foundation": "^5.4|^6.4|^7.3|^8" + }, + "require-dev": { + "phpstan/phpstan": "^2", + "phpunit/phpunit": "^9", + "squizlabs/php_codesniffer": "^4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Fruitcake\\Cors\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fruitcake", + "homepage": "https://fruitcake.nl" + }, + { + "name": "Barryvdh", + "email": "barryvdh@gmail.com" + } + ], + "description": "Cross-origin resource sharing library for the Symfony HttpFoundation", + "homepage": "https://github.com/fruitcake/php-cors", + "keywords": [ + "cors", + "laravel", + "symfony" + ], + "support": { + "issues": "https://github.com/fruitcake/php-cors/issues", + "source": "https://github.com/fruitcake/php-cors/tree/v1.4.0" + }, + "funding": [ + { + "url": "https://fruitcake.nl", + "type": "custom" + }, + { + "url": "https://github.com/barryvdh", + "type": "github" + } + ], + "time": "2025-12-03T09:33:47+00:00" + }, + { + "name": "graham-campbell/result-type", + "version": "v1.1.4", + "source": { + "type": "git", + "url": "https://github.com/GrahamCampbell/Result-Type.git", + "reference": "e01f4a821471308ba86aa202fed6698b6b695e3b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/e01f4a821471308ba86aa202fed6698b6b695e3b", + "reference": "e01f4a821471308ba86aa202fed6698b6b695e3b", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "phpoption/phpoption": "^1.9.5" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.41 || ^9.6.22 || ^10.5.45 || ^11.5.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "GrahamCampbell\\ResultType\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "An Implementation Of The Result Type", + "keywords": [ + "Graham Campbell", + "GrahamCampbell", + "Result Type", + "Result-Type", + "result" + ], + "support": { + "issues": "https://github.com/GrahamCampbell/Result-Type/issues", + "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.4" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type", + "type": "tidelift" + } + ], + "time": "2025-12-27T19:43:20+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "7.12.3", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "9aa17bcdd777ee31df9fc83c337ca4ca2340def3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/9aa17bcdd777ee31df9fc83c337ca4ca2340def3", + "reference": "9aa17bcdd777ee31df9fc83c337ca4ca2340def3", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^2.5", + "guzzlehttp/psr7": "^2.12.3", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.5 || ^3.0", + "symfony/polyfill-php80": "^1.25" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-curl": "*", + "guzzle/client-integration-tests": "3.0.2", + "guzzlehttp/test-server": "^0.5.1", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^8.5.52 || ^9.6.34", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.12.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2026-06-23T15:29:02+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "2.5.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "4360e982f87f5f258bf872d094647791db2f4c8e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/4360e982f87f5f258bf872d094647791db2f4c8e", + "reference": "4360e982f87f5f258bf872d094647791db2f4c8e", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "symfony/deprecation-contracts": "^2.5 || ^3.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.52 || ^9.6.34" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/2.5.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2026-06-02T12:23:43+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.12.3", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "7ec62dc3f44aa218487dbed81a9bf9bc647be55d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/7ec62dc3f44aa218487dbed81a9bf9bc647be55d", + "reference": "7ec62dc3f44aa218487dbed81a9bf9bc647be55d", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0", + "symfony/deprecation-contracts": "^2.5 || ^3.0", + "symfony/polyfill-php80": "^1.25" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "http-interop/http-factory-tests": "1.1.0", + "jshttp/mime-db": "1.54.0.1", + "phpunit/phpunit": "^8.5.52 || ^9.6.34" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.12.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2026-06-23T15:21:08+00:00" + }, + { + "name": "guzzlehttp/uri-template", + "version": "v1.0.8", + "source": { + "type": "git", + "url": "https://github.com/guzzle/uri-template.git", + "reference": "9c19128923b05a5d7355e5d2318d7808b7e33bbd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/uri-template/zipball/9c19128923b05a5d7355e5d2318d7808b7e33bbd", + "reference": "9c19128923b05a5d7355e5d2318d7808b7e33bbd", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "symfony/polyfill-php80": "^1.25" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.52 || ^9.6.34", + "uri-template/tests": "1.0.0" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\UriTemplate\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + } + ], + "description": "A polyfill class for uri_template of PHP", + "keywords": [ + "guzzlehttp", + "uri-template" + ], + "support": { + "issues": "https://github.com/guzzle/uri-template/issues", + "source": "https://github.com/guzzle/uri-template/tree/v1.0.8" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/uri-template", + "type": "tidelift" + } + ], + "time": "2026-06-23T13:02:23+00:00" + }, + { + "name": "laravel/framework", + "version": "v13.17.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/framework.git", + "reference": "0802b7a81f3252d78200b8037ac183a686a529f0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/framework/zipball/0802b7a81f3252d78200b8037ac183a686a529f0", + "reference": "0802b7a81f3252d78200b8037ac183a686a529f0", + "shasum": "" + }, + "require": { + "brick/math": "^0.14.2 || ^0.15 || ^0.16 || ^0.17 || ^0.18", + "composer-runtime-api": "^2.2", + "doctrine/inflector": "^2.0.5", + "dragonmantank/cron-expression": "^3.4", + "egulias/email-validator": "^4.0", + "ext-ctype": "*", + "ext-filter": "*", + "ext-hash": "*", + "ext-mbstring": "*", + "ext-openssl": "*", + "ext-session": "*", + "ext-tokenizer": "*", + "fruitcake/php-cors": "^1.3", + "guzzlehttp/guzzle": "^7.8.2", + "guzzlehttp/promises": "^2.0.3", + "guzzlehttp/uri-template": "^1.0", + "laravel/prompts": "^0.3.0", + "laravel/serializable-closure": "^2.0.10", + "league/commonmark": "^2.8.1", + "league/flysystem": "^3.25.1", + "league/flysystem-local": "^3.25.1", + "league/uri": "^7.5.1", + "monolog/monolog": "^3.0", + "nesbot/carbon": "^3.8.4", + "nunomaduro/termwind": "^2.0", + "php": "^8.3", + "psr/container": "^1.1.1 || ^2.0.1", + "psr/log": "^1.0 || ^2.0 || ^3.0", + "psr/simple-cache": "^1.0 || ^2.0 || ^3.0", + "ramsey/uuid": "^4.7", + "symfony/console": "^7.4.0 || ^8.0.0", + "symfony/error-handler": "^7.4.0 || ^8.0.0", + "symfony/finder": "^7.4.0 || ^8.0.0", + "symfony/http-foundation": "^7.4.0 || ^8.0.0", + "symfony/http-kernel": "^7.4.0 || ^8.0.0", + "symfony/mailer": "^7.4.0 || ^8.0.0", + "symfony/mime": "^7.4.0 || ^8.0.0", + "symfony/polyfill-php84": "^1.36", + "symfony/polyfill-php85": "^1.36", + "symfony/polyfill-php86": "^1.36", + "symfony/process": "^7.4.5 || ^8.0.5", + "symfony/routing": "^7.4.0 || ^8.0.0", + "symfony/uid": "^7.4.0 || ^8.0.0", + "symfony/var-dumper": "^7.4.0 || ^8.0.0", + "tijsverkoyen/css-to-inline-styles": "^2.2.5", + "vlucas/phpdotenv": "^5.6.1", + "voku/portable-ascii": "^2.0.2" + }, + "conflict": { + "tightenco/collect": "<5.5.33" + }, + "provide": { + "psr/container-implementation": "1.1 || 2.0", + "psr/log-implementation": "1.0 || 2.0 || 3.0", + "psr/simple-cache-implementation": "1.0 || 2.0 || 3.0" + }, + "replace": { + "illuminate/auth": "self.version", + "illuminate/broadcasting": "self.version", + "illuminate/bus": "self.version", + "illuminate/cache": "self.version", + "illuminate/collections": "self.version", + "illuminate/concurrency": "self.version", + "illuminate/conditionable": "self.version", + "illuminate/config": "self.version", + "illuminate/console": "self.version", + "illuminate/container": "self.version", + "illuminate/contracts": "self.version", + "illuminate/cookie": "self.version", + "illuminate/database": "self.version", + "illuminate/encryption": "self.version", + "illuminate/events": "self.version", + "illuminate/filesystem": "self.version", + "illuminate/hashing": "self.version", + "illuminate/http": "self.version", + "illuminate/json-schema": "self.version", + "illuminate/log": "self.version", + "illuminate/macroable": "self.version", + "illuminate/mail": "self.version", + "illuminate/notifications": "self.version", + "illuminate/pagination": "self.version", + "illuminate/pipeline": "self.version", + "illuminate/process": "self.version", + "illuminate/queue": "self.version", + "illuminate/redis": "self.version", + "illuminate/reflection": "self.version", + "illuminate/routing": "self.version", + "illuminate/session": "self.version", + "illuminate/support": "self.version", + "illuminate/testing": "self.version", + "illuminate/translation": "self.version", + "illuminate/validation": "self.version", + "illuminate/view": "self.version", + "spatie/once": "*" + }, + "require-dev": { + "ably/ably-php": "^1.0", + "aws/aws-sdk-php": "^3.322.9", + "ext-gmp": "*", + "fakerphp/faker": "^1.24", + "guzzlehttp/psr7": "^2.9", + "laravel/pint": "^1.18", + "league/flysystem-aws-s3-v3": "^3.25.1", + "league/flysystem-ftp": "^3.25.1", + "league/flysystem-path-prefixing": "^3.25.1", + "league/flysystem-read-only": "^3.25.1", + "league/flysystem-sftp-v3": "^3.25.1", + "mockery/mockery": "^1.6.10", + "opis/json-schema": "^2.4.1", + "orchestra/testbench-core": "^11.0.0", + "pda/pheanstalk": "^7.0.0 || ^8.0.0", + "php-http/discovery": "^1.15", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^11.5.50 || ^12.5.8 || ^13.0.3", + "predis/predis": "^2.3 || ^3.0", + "rector/rector": "^2.3", + "resend/resend-php": "^1.0", + "symfony/cache": "^7.4.0 || ^8.0.0", + "symfony/http-client": "^7.4.0 || ^8.0.0", + "symfony/psr-http-message-bridge": "^7.4.0 || ^8.0.0", + "symfony/translation": "^7.4.0 || ^8.0.0" + }, + "suggest": { + "ably/ably-php": "Required to use the Ably broadcast driver (^1.0).", + "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage, and SES mail driver (^3.322.9).", + "brianium/paratest": "Required to run tests in parallel (^7.0 || ^8.0).", + "ext-apcu": "Required to use the APC cache driver.", + "ext-fileinfo": "Required to use the Filesystem class.", + "ext-ftp": "Required to use the Flysystem FTP driver.", + "ext-gd": "Required to use Illuminate\\Http\\Testing\\FileFactory::image().", + "ext-memcached": "Required to use the memcache cache driver.", + "ext-pcntl": "Required to use all features of the queue worker and console signal trapping.", + "ext-pdo": "Required to use all database features.", + "ext-posix": "Required to use all features of the queue worker.", + "ext-redis": "Required to use the Redis cache and queue drivers (^4.0 || ^5.0 || ^6.0).", + "fakerphp/faker": "Required to generate fake data using the fake() helper (^1.23).", + "filp/whoops": "Required for friendly error pages in development (^2.14.3).", + "laravel/tinker": "Required to use the tinker console command (^2.0).", + "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.25.1).", + "league/flysystem-ftp": "Required to use the Flysystem FTP driver (^3.25.1).", + "league/flysystem-path-prefixing": "Required to use the scoped driver (^3.25.1).", + "league/flysystem-read-only": "Required to use read-only disks (^3.25.1)", + "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.25.1).", + "mockery/mockery": "Required to use mocking (^1.6).", + "pda/pheanstalk": "Required to use the beanstalk queue driver (^7.0 || ^8.0).", + "php-http/discovery": "Required to use PSR-7 bridging features (^1.15).", + "phpunit/phpunit": "Required to use assertions and run tests (^11.5.50 || ^12.5.8 || ^13.0.3).", + "predis/predis": "Required to use the predis connector (^2.3 || ^3.0).", + "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", + "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^6.0 || ^7.0).", + "resend/resend-php": "Required to enable support for the Resend mail transport (^0.10.0 || ^1.0).", + "spatie/fork": "Required to use the 'fork' concurrency driver (^1.2).", + "symfony/cache": "Required to PSR-6 cache bridge (^7.4 || ^8.0).", + "symfony/filesystem": "Required to enable support for relative symbolic links (^7.4 || ^8.0).", + "symfony/http-client": "Required to enable support for the Symfony API mail transports (^7.4 || ^8.0).", + "symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^7.4 || ^8.0).", + "symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^7.4 || ^8.0).", + "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^7.4 || ^8.0)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "13.0.x-dev" + } + }, + "autoload": { + "files": [ + "src/Illuminate/Collections/functions.php", + "src/Illuminate/Collections/helpers.php", + "src/Illuminate/Events/functions.php", + "src/Illuminate/Filesystem/functions.php", + "src/Illuminate/Foundation/helpers.php", + "src/Illuminate/Log/functions.php", + "src/Illuminate/Reflection/helpers.php", + "src/Illuminate/Support/functions.php", + "src/Illuminate/Support/helpers.php" + ], + "psr-4": { + "Illuminate\\": "src/Illuminate/", + "Illuminate\\Support\\": [ + "src/Illuminate/Macroable/", + "src/Illuminate/Collections/", + "src/Illuminate/Conditionable/", + "src/Illuminate/Reflection/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Laravel Framework.", + "homepage": "https://laravel.com", + "keywords": [ + "framework", + "laravel" + ], + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2026-06-23T19:42:45+00:00" + }, + { + "name": "laravel/prompts", + "version": "v0.3.19", + "source": { + "type": "git", + "url": "https://github.com/laravel/prompts.git", + "reference": "ac8ac2c3ec598df031dcd474a002a51b7b6e69a5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/prompts/zipball/ac8ac2c3ec598df031dcd474a002a51b7b6e69a5", + "reference": "ac8ac2c3ec598df031dcd474a002a51b7b6e69a5", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.2", + "ext-mbstring": "*", + "php": "^8.1", + "symfony/console": "^6.2|^7.0|^8.0" + }, + "conflict": { + "illuminate/console": ">=10.17.0 <10.25.0", + "laravel/framework": ">=10.17.0 <10.25.0" + }, + "require-dev": { + "illuminate/collections": "^10.0|^11.0|^12.0|^13.0", + "mockery/mockery": "^1.5", + "pestphp/pest": "^2.3|^3.4|^4.0", + "phpstan/phpstan": "^1.12.28", + "phpstan/phpstan-mockery": "^1.1.3" + }, + "suggest": { + "ext-pcntl": "Required for the spinner to be animated." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "0.3.x-dev" + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Laravel\\Prompts\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Add beautiful and user-friendly forms to your command-line applications.", + "support": { + "issues": "https://github.com/laravel/prompts/issues", + "source": "https://github.com/laravel/prompts/tree/v0.3.19" + }, + "time": "2026-06-23T01:06:19+00:00" + }, + { + "name": "laravel/sanctum", + "version": "v4.3.2", + "source": { + "type": "git", + "url": "https://github.com/laravel/sanctum.git", + "reference": "2a9bccc18e9907808e0018dd15fa643937886b1e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/sanctum/zipball/2a9bccc18e9907808e0018dd15fa643937886b1e", + "reference": "2a9bccc18e9907808e0018dd15fa643937886b1e", + "shasum": "" + }, + "require": { + "ext-json": "*", + "illuminate/console": "^11.0|^12.0|^13.0", + "illuminate/contracts": "^11.0|^12.0|^13.0", + "illuminate/database": "^11.0|^12.0|^13.0", + "illuminate/support": "^11.0|^12.0|^13.0", + "php": "^8.2", + "symfony/console": "^7.0|^8.0" + }, + "require-dev": { + "mockery/mockery": "^1.6", + "orchestra/testbench": "^9.15|^10.8|^11.0", + "phpstan/phpstan": "^1.10" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Sanctum\\SanctumServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Sanctum\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Laravel Sanctum provides a featherweight authentication system for SPAs and simple APIs.", + "keywords": [ + "auth", + "laravel", + "sanctum" + ], + "support": { + "issues": "https://github.com/laravel/sanctum/issues", + "source": "https://github.com/laravel/sanctum" + }, + "time": "2026-04-30T11:46:25+00:00" + }, + { + "name": "laravel/serializable-closure", + "version": "v2.0.13", + "source": { + "type": "git", + "url": "https://github.com/laravel/serializable-closure.git", + "reference": "b566ee0dd251f3c4078bed003a7ce015f5ea6dce" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/b566ee0dd251f3c4078bed003a7ce015f5ea6dce", + "reference": "b566ee0dd251f3c4078bed003a7ce015f5ea6dce", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "illuminate/support": "^10.0|^11.0|^12.0|^13.0", + "nesbot/carbon": "^2.67|^3.0", + "pestphp/pest": "^2.36|^3.0|^4.0", + "phpstan/phpstan": "^2.0", + "symfony/var-dumper": "^6.2.0|^7.0.0|^8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\SerializableClosure\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Nuno Maduro", + "email": "nuno@laravel.com" + } + ], + "description": "Laravel Serializable Closure provides an easy and secure way to serialize closures in PHP.", + "keywords": [ + "closure", + "laravel", + "serializable" + ], + "support": { + "issues": "https://github.com/laravel/serializable-closure/issues", + "source": "https://github.com/laravel/serializable-closure" + }, + "time": "2026-04-16T14:03:50+00:00" + }, + { + "name": "laravel/socialite", + "version": "v5.28.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/socialite.git", + "reference": "4c131ff4b24d8881a9c8fe4eecb5ffeff9803f26" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/socialite/zipball/4c131ff4b24d8881a9c8fe4eecb5ffeff9803f26", + "reference": "4c131ff4b24d8881a9c8fe4eecb5ffeff9803f26", + "shasum": "" + }, + "require": { + "ext-json": "*", + "firebase/php-jwt": "^6.4|^7.0", + "guzzlehttp/guzzle": "^6.0|^7.0", + "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/http": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", + "league/oauth1-client": "^1.11", + "php": "^7.2|^8.0", + "phpseclib/phpseclib": "^3.0" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "orchestra/testbench": "^4.18|^5.20|^6.47|^7.55|^8.36|^9.15|^10.8|^11.0", + "phpstan/phpstan": "^1.12.23", + "phpunit/phpunit": "^8.0|^9.3|^10.4|^11.5|^12.0" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Socialite": "Laravel\\Socialite\\Facades\\Socialite" + }, + "providers": [ + "Laravel\\Socialite\\SocialiteServiceProvider" + ] + }, + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\Socialite\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Laravel wrapper around OAuth 1 & OAuth 2 libraries.", + "homepage": "https://laravel.com", + "keywords": [ + "laravel", + "oauth" + ], + "support": { + "issues": "https://github.com/laravel/socialite/issues", + "source": "https://github.com/laravel/socialite" + }, + "time": "2026-06-12T03:24:05+00:00" + }, + { + "name": "laravel/tinker", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/laravel/tinker.git", + "reference": "4faba77764bd33411735936acdf30446d058c78b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/tinker/zipball/4faba77764bd33411735936acdf30446d058c78b", + "reference": "4faba77764bd33411735936acdf30446d058c78b", + "shasum": "" + }, + "require": { + "illuminate/console": "^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/contracts": "^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/support": "^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", + "php": "^8.1", + "psy/psysh": "^0.12.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0|^8.0" + }, + "require-dev": { + "mockery/mockery": "~1.3.3|^1.4.2", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.5|^11.5" + }, + "suggest": { + "illuminate/database": "The Illuminate Database package (^8.0|^9.0|^10.0|^11.0|^12.0|^13.0)." + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Tinker\\TinkerServiceProvider" + ] + }, + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\Tinker\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Powerful REPL for the Laravel framework.", + "keywords": [ + "REPL", + "Tinker", + "laravel", + "psysh" + ], + "support": { + "issues": "https://github.com/laravel/tinker/issues", + "source": "https://github.com/laravel/tinker/tree/v3.0.2" + }, + "time": "2026-03-17T14:54:13+00:00" + }, + { + "name": "league/commonmark", + "version": "2.8.2", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/commonmark.git", + "reference": "59fb075d2101740c337c7216e3f32b36c204218b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/59fb075d2101740c337c7216e3f32b36c204218b", + "reference": "59fb075d2101740c337c7216e3f32b36c204218b", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "league/config": "^1.1.1", + "php": "^7.4 || ^8.0", + "psr/event-dispatcher": "^1.0", + "symfony/deprecation-contracts": "^2.1 || ^3.0", + "symfony/polyfill-php80": "^1.16" + }, + "require-dev": { + "cebe/markdown": "^1.0", + "commonmark/cmark": "0.31.1", + "commonmark/commonmark.js": "0.31.1", + "composer/package-versions-deprecated": "^1.8", + "embed/embed": "^4.4", + "erusev/parsedown": "^1.0", + "ext-json": "*", + "github/gfm": "0.29.0", + "michelf/php-markdown": "^1.4 || ^2.0", + "nyholm/psr7": "^1.5", + "phpstan/phpstan": "^1.8.2", + "phpunit/phpunit": "^9.5.21 || ^10.5.9 || ^11.0.0", + "scrutinizer/ocular": "^1.8.1", + "symfony/finder": "^5.3 | ^6.0 | ^7.0 || ^8.0", + "symfony/process": "^5.4 | ^6.0 | ^7.0 || ^8.0", + "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 | ^7.0 || ^8.0", + "unleashedtech/php-coding-standard": "^3.1.1", + "vimeo/psalm": "^4.24.0 || ^5.0.0 || ^6.0.0" + }, + "suggest": { + "symfony/yaml": "v2.3+ required if using the Front Matter extension" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.9-dev" + } + }, + "autoload": { + "psr-4": { + "League\\CommonMark\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + } + ], + "description": "Highly-extensible PHP Markdown parser which fully supports the CommonMark spec and GitHub-Flavored Markdown (GFM)", + "homepage": "https://commonmark.thephpleague.com", + "keywords": [ + "commonmark", + "flavored", + "gfm", + "github", + "github-flavored", + "markdown", + "md", + "parser" + ], + "support": { + "docs": "https://commonmark.thephpleague.com/", + "forum": "https://github.com/thephpleague/commonmark/discussions", + "issues": "https://github.com/thephpleague/commonmark/issues", + "rss": "https://github.com/thephpleague/commonmark/releases.atom", + "source": "https://github.com/thephpleague/commonmark" + }, + "funding": [ + { + "url": "https://www.colinodell.com/sponsor", + "type": "custom" + }, + { + "url": "https://www.paypal.me/colinpodell/10.00", + "type": "custom" + }, + { + "url": "https://github.com/colinodell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/commonmark", + "type": "tidelift" + } + ], + "time": "2026-03-19T13:16:38+00:00" + }, + { + "name": "league/config", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/config.git", + "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/config/zipball/754b3604fb2984c71f4af4a9cbe7b57f346ec1f3", + "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3", + "shasum": "" + }, + "require": { + "dflydev/dot-access-data": "^3.0.1", + "nette/schema": "^1.2", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.8.2", + "phpunit/phpunit": "^9.5.5", + "scrutinizer/ocular": "^1.8.1", + "unleashedtech/php-coding-standard": "^3.1", + "vimeo/psalm": "^4.7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.2-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Config\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + } + ], + "description": "Define configuration arrays with strict schemas and access values with dot notation", + "homepage": "https://config.thephpleague.com", + "keywords": [ + "array", + "config", + "configuration", + "dot", + "dot-access", + "nested", + "schema" + ], + "support": { + "docs": "https://config.thephpleague.com/", + "issues": "https://github.com/thephpleague/config/issues", + "rss": "https://github.com/thephpleague/config/releases.atom", + "source": "https://github.com/thephpleague/config" + }, + "funding": [ + { + "url": "https://www.colinodell.com/sponsor", + "type": "custom" + }, + { + "url": "https://www.paypal.me/colinpodell/10.00", + "type": "custom" + }, + { + "url": "https://github.com/colinodell", + "type": "github" + } + ], + "time": "2022-12-11T20:36:23+00:00" + }, + { + "name": "league/flysystem", + "version": "3.35.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem.git", + "reference": "d0a405f03d980461e4b6e08cfed2c162650f9f6b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/d0a405f03d980461e4b6e08cfed2c162650f9f6b", + "reference": "d0a405f03d980461e4b6e08cfed2c162650f9f6b", + "shasum": "" + }, + "require": { + "league/flysystem-local": "^3.0.0", + "league/mime-type-detection": "^1.0.0", + "php": "^8.0.2" + }, + "conflict": { + "async-aws/core": "<1.19.0", + "async-aws/s3": "<1.14.0", + "aws/aws-sdk-php": "3.209.31 || 3.210.0", + "guzzlehttp/guzzle": "<7.0", + "guzzlehttp/ringphp": "<1.1.1", + "phpseclib/phpseclib": "3.0.15", + "symfony/http-client": "<5.2" + }, + "require-dev": { + "async-aws/s3": "^1.5 || ^2.0", + "async-aws/simple-s3": "^1.1 || ^2.0", + "aws/aws-sdk-php": "^3.295.10", + "composer/semver": "^3.0", + "ext-fileinfo": "*", + "ext-ftp": "*", + "ext-mongodb": "^1.3|^2", + "ext-zip": "*", + "friendsofphp/php-cs-fixer": "^3.5", + "google/cloud-storage": "^1.23", + "guzzlehttp/psr7": "^2.6", + "microsoft/azure-storage-blob": "^1.1", + "mongodb/mongodb": "^1.2|^2", + "phpseclib/phpseclib": "^3.0.36", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5.11|^10.0", + "sabre/dav": "^4.6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "File storage abstraction for PHP", + "keywords": [ + "WebDAV", + "aws", + "cloud", + "file", + "files", + "filesystem", + "filesystems", + "ftp", + "s3", + "sftp", + "storage" + ], + "support": { + "issues": "https://github.com/thephpleague/flysystem/issues", + "source": "https://github.com/thephpleague/flysystem/tree/3.35.0" + }, + "time": "2026-06-22T20:12:06+00:00" + }, + { + "name": "league/flysystem-local", + "version": "3.31.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem-local.git", + "reference": "2f669db18a4c20c755c2bb7d3a7b0b2340488079" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/2f669db18a4c20c755c2bb7d3a7b0b2340488079", + "reference": "2f669db18a4c20c755c2bb7d3a7b0b2340488079", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "league/flysystem": "^3.0.0", + "league/mime-type-detection": "^1.0.0", + "php": "^8.0.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\Local\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "Local filesystem adapter for Flysystem.", + "keywords": [ + "Flysystem", + "file", + "files", + "filesystem", + "local" + ], + "support": { + "source": "https://github.com/thephpleague/flysystem-local/tree/3.31.0" + }, + "time": "2026-01-23T15:30:45+00:00" + }, + { + "name": "league/mime-type-detection", + "version": "1.16.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/mime-type-detection.git", + "reference": "2d6702ff215bf922936ccc1ad31007edc76451b9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/2d6702ff215bf922936ccc1ad31007edc76451b9", + "reference": "2d6702ff215bf922936ccc1ad31007edc76451b9", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.2", + "phpstan/phpstan": "^0.12.68", + "phpunit/phpunit": "^8.5.8 || ^9.3 || ^10.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\MimeTypeDetection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "Mime-type detection for Flysystem", + "support": { + "issues": "https://github.com/thephpleague/mime-type-detection/issues", + "source": "https://github.com/thephpleague/mime-type-detection/tree/1.16.0" + }, + "funding": [ + { + "url": "https://github.com/frankdejonge", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/flysystem", + "type": "tidelift" + } + ], + "time": "2024-09-21T08:32:55+00:00" + }, + { + "name": "league/oauth1-client", + "version": "v1.11.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/oauth1-client.git", + "reference": "f9c94b088837eb1aae1ad7c4f23eb65cc6993055" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/oauth1-client/zipball/f9c94b088837eb1aae1ad7c4f23eb65cc6993055", + "reference": "f9c94b088837eb1aae1ad7c4f23eb65cc6993055", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-openssl": "*", + "guzzlehttp/guzzle": "^6.0|^7.0", + "guzzlehttp/psr7": "^1.7|^2.0", + "php": ">=7.1||>=8.0" + }, + "require-dev": { + "ext-simplexml": "*", + "friendsofphp/php-cs-fixer": "^2.17", + "mockery/mockery": "^1.3.3", + "phpstan/phpstan": "^0.12.42", + "phpunit/phpunit": "^7.5||9.5" + }, + "suggest": { + "ext-simplexml": "For decoding XML-based responses." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev", + "dev-develop": "2.0-dev" + } + }, + "autoload": { + "psr-4": { + "League\\OAuth1\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ben Corlett", + "email": "bencorlett@me.com", + "homepage": "http://www.webcomm.com.au", + "role": "Developer" + } + ], + "description": "OAuth 1.0 Client Library", + "keywords": [ + "Authentication", + "SSO", + "authorization", + "bitbucket", + "identity", + "idp", + "oauth", + "oauth1", + "single sign on", + "trello", + "tumblr", + "twitter" + ], + "support": { + "issues": "https://github.com/thephpleague/oauth1-client/issues", + "source": "https://github.com/thephpleague/oauth1-client/tree/v1.11.0" + }, + "time": "2024-12-10T19:59:05+00:00" + }, + { + "name": "league/uri", + "version": "7.8.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri.git", + "reference": "08cf38e3924d4f56238125547b5720496fac8fd4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri/zipball/08cf38e3924d4f56238125547b5720496fac8fd4", + "reference": "08cf38e3924d4f56238125547b5720496fac8fd4", + "shasum": "" + }, + "require": { + "league/uri-interfaces": "^7.8.1", + "php": "^8.1", + "psr/http-factory": "^1" + }, + "conflict": { + "league/uri-schemes": "^1.0" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-dom": "to convert the URI into an HTML anchor tag", + "ext-fileinfo": "to create Data URI from file contennts", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "ext-uri": "to use the PHP native URI class", + "jeremykendall/php-domain-parser": "to further parse the URI host and resolve its Public Suffix and Top Level Domain", + "league/uri-components": "to provide additional tools to manipulate URI objects components", + "league/uri-polyfill": "to backport the PHP URI extension for older versions of PHP", + "php-64bit": "to improve IPV4 host parsing", + "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Uri\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "URI manipulation library", + "homepage": "https://uri.thephpleague.com", + "keywords": [ + "URN", + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "middleware", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc2141", + "rfc3986", + "rfc3987", + "rfc6570", + "rfc8141", + "uri", + "uri-template", + "url", + "ws" + ], + "support": { + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri/tree/7.8.1" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2026-03-15T20:22:25+00:00" + }, + { + "name": "league/uri-interfaces", + "version": "7.8.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri-interfaces.git", + "reference": "85d5c77c5d6d3af6c54db4a78246364908f3c928" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/85d5c77c5d6d3af6c54db4a78246364908f3c928", + "reference": "85d5c77c5d6d3af6c54db4a78246364908f3c928", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^8.1", + "psr/http-message": "^1.1 || ^2.0" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "php-64bit": "to improve IPV4 host parsing", + "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Uri\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "Common tools for parsing and resolving RFC3987/RFC3986 URI", + "homepage": "https://uri.thephpleague.com", + "keywords": [ + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc3986", + "rfc3987", + "rfc6570", + "uri", + "url", + "ws" + ], + "support": { + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri-interfaces/tree/7.8.1" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2026-03-08T20:05:35+00:00" + }, + { + "name": "monolog/monolog", + "version": "3.10.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "b321dd6749f0bf7189444158a3ce785cc16d69b0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/b321dd6749f0bf7189444158a3ce785cc16d69b0", + "reference": "b321dd6749f0bf7189444158a3ce785cc16d69b0", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/log": "^2.0 || ^3.0" + }, + "provide": { + "psr/log-implementation": "3.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "^3.0", + "doctrine/couchdb": "~1.0@dev", + "elasticsearch/elasticsearch": "^7 || ^8", + "ext-json": "*", + "graylog2/gelf-php": "^1.4.2 || ^2.0", + "guzzlehttp/guzzle": "^7.4.5", + "guzzlehttp/psr7": "^2.2", + "mongodb/mongodb": "^1.8 || ^2.0", + "php-amqplib/php-amqplib": "~2.4 || ^3", + "php-console/php-console": "^3.1.8", + "phpstan/phpstan": "^2", + "phpstan/phpstan-deprecation-rules": "^2", + "phpstan/phpstan-strict-rules": "^2", + "phpunit/phpunit": "^10.5.17 || ^11.0.7", + "predis/predis": "^1.1 || ^2", + "rollbar/rollbar": "^4.0", + "ruflin/elastica": "^7 || ^8", + "symfony/mailer": "^5.4 || ^6", + "symfony/mime": "^5.4 || ^6" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler", + "ext-mbstring": "Allow to work properly with unicode symbols", + "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)", + "ext-openssl": "Required to send log messages using SSL", + "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Monolog\\": "src/Monolog" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "https://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "support": { + "issues": "https://github.com/Seldaek/monolog/issues", + "source": "https://github.com/Seldaek/monolog/tree/3.10.0" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", + "type": "tidelift" + } + ], + "time": "2026-01-02T08:56:05+00:00" + }, + { + "name": "nesbot/carbon", + "version": "3.13.0", + "source": { + "type": "git", + "url": "https://github.com/CarbonPHP/carbon.git", + "reference": "40f6618f052df16b545f626fbf9a878e6497d16a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/40f6618f052df16b545f626fbf9a878e6497d16a", + "reference": "40f6618f052df16b545f626fbf9a878e6497d16a", + "shasum": "" + }, + "require": { + "carbonphp/carbon-doctrine-types": "<100.0", + "ext-json": "*", + "php": "^8.1", + "psr/clock": "^1.0", + "symfony/clock": "^6.3.12 || ^7.0 || ^8.0", + "symfony/polyfill-mbstring": "^1.0", + "symfony/translation": "^4.4.18 || ^5.2.1 || ^6.0 || ^7.0 || ^8.0" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "require-dev": { + "doctrine/dbal": "^3.6.3 || ^4.0", + "doctrine/orm": "^2.15.2 || ^3.0", + "friendsofphp/php-cs-fixer": "^v3.87.1", + "kylekatarnls/multi-tester": "^2.5.3", + "phpmd/phpmd": "^2.15.0", + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^2.1.22", + "phpunit/phpunit": "^10.5.53", + "squizlabs/php_codesniffer": "^3.13.4 || ^4.0.0" + }, + "bin": [ + "bin/carbon" + ], + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Carbon\\Laravel\\ServiceProvider" + ] + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + }, + "branch-alias": { + "dev-2.x": "2.x-dev", + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Carbon\\": "src/Carbon/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Nesbitt", + "email": "brian@nesbot.com", + "homepage": "https://markido.com" + }, + { + "name": "kylekatarnls", + "homepage": "https://github.com/kylekatarnls" + } + ], + "description": "An API extension for DateTime that supports 281 different languages.", + "homepage": "https://carbonphp.github.io/carbon/", + "keywords": [ + "date", + "datetime", + "time" + ], + "support": { + "docs": "https://carbonphp.github.io/carbon/guide/getting-started/introduction.html", + "issues": "https://github.com/CarbonPHP/carbon/issues", + "source": "https://github.com/CarbonPHP/carbon" + }, + "funding": [ + { + "url": "https://github.com/sponsors/kylekatarnls", + "type": "github" + }, + { + "url": "https://opencollective.com/Carbon#sponsor", + "type": "opencollective" + }, + { + "url": "https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme", + "type": "tidelift" + } + ], + "time": "2026-06-18T13:49:15+00:00" + }, + { + "name": "nette/schema", + "version": "v1.3.5", + "source": { + "type": "git", + "url": "https://github.com/nette/schema.git", + "reference": "f0ab1a3cda782dbc5da270d28545236aa80c4002" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/schema/zipball/f0ab1a3cda782dbc5da270d28545236aa80c4002", + "reference": "f0ab1a3cda782dbc5da270d28545236aa80c4002", + "shasum": "" + }, + "require": { + "nette/utils": "^4.0", + "php": "8.1 - 8.5" + }, + "require-dev": { + "nette/phpstan-rules": "^1.0", + "nette/tester": "^2.6", + "phpstan/extension-installer": "^1.4@stable", + "phpstan/phpstan": "^2.1.39@stable", + "tracy/tracy": "^2.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Nette\\": "src" + }, + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "📐 Nette Schema: validating data structures against a given Schema.", + "homepage": "https://nette.org", + "keywords": [ + "config", + "nette" + ], + "support": { + "issues": "https://github.com/nette/schema/issues", + "source": "https://github.com/nette/schema/tree/v1.3.5" + }, + "time": "2026-02-23T03:47:12+00:00" + }, + { + "name": "nette/utils", + "version": "v4.1.4", + "source": { + "type": "git", + "url": "https://github.com/nette/utils.git", + "reference": "7da6c396d7ebe142bc857c20479d5e70a5e1aac7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/utils/zipball/7da6c396d7ebe142bc857c20479d5e70a5e1aac7", + "reference": "7da6c396d7ebe142bc857c20479d5e70a5e1aac7", + "shasum": "" + }, + "require": { + "php": "8.2 - 8.5" + }, + "conflict": { + "nette/finder": "<3", + "nette/schema": "<1.2.2" + }, + "require-dev": { + "jetbrains/phpstorm-attributes": "^1.2", + "nette/phpstan-rules": "^1.0", + "nette/tester": "^2.5", + "phpstan/extension-installer": "^1.4@stable", + "phpstan/phpstan": "^2.1@stable", + "tracy/tracy": "^2.9" + }, + "suggest": { + "ext-gd": "to use Image", + "ext-iconv": "to use Strings::webalize(), toAscii(), chr() and reverse()", + "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()", + "ext-json": "to use Nette\\Utils\\Json", + "ext-mbstring": "to use Strings::lower() etc...", + "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "autoload": { + "psr-4": { + "Nette\\": "src" + }, + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.", + "homepage": "https://nette.org", + "keywords": [ + "array", + "core", + "datetime", + "images", + "json", + "nette", + "paginator", + "password", + "slugify", + "string", + "unicode", + "utf-8", + "utility", + "validation" + ], + "support": { + "issues": "https://github.com/nette/utils/issues", + "source": "https://github.com/nette/utils/tree/v4.1.4" + }, + "time": "2026-05-11T20:49:54+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.7.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" + }, + "time": "2025-12-06T11:56:16+00:00" + }, + { + "name": "nunomaduro/termwind", + "version": "v2.4.0", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/termwind.git", + "reference": "712a31b768f5daea284c2169a7d227031001b9a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/712a31b768f5daea284c2169a7d227031001b9a8", + "reference": "712a31b768f5daea284c2169a7d227031001b9a8", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^8.2", + "symfony/console": "^7.4.4 || ^8.0.4" + }, + "require-dev": { + "illuminate/console": "^11.47.0", + "laravel/pint": "^1.27.1", + "mockery/mockery": "^1.6.12", + "pestphp/pest": "^2.36.0 || ^3.8.4 || ^4.3.2", + "phpstan/phpstan": "^1.12.32", + "phpstan/phpstan-strict-rules": "^1.6.2", + "symfony/var-dumper": "^7.3.5 || ^8.0.4", + "thecodingmachine/phpstan-strict-rules": "^1.0.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Termwind\\Laravel\\TermwindServiceProvider" + ] + }, + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "files": [ + "src/Functions.php" + ], + "psr-4": { + "Termwind\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "It's like Tailwind CSS, but for the console.", + "keywords": [ + "cli", + "console", + "css", + "package", + "php", + "style" + ], + "support": { + "issues": "https://github.com/nunomaduro/termwind/issues", + "source": "https://github.com/nunomaduro/termwind/tree/v2.4.0" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://github.com/xiCO2k", + "type": "github" + } + ], + "time": "2026-02-16T23:10:27+00:00" + }, + { + "name": "onesignal/onesignal-php-api", + "version": "5.8.0", + "source": { + "type": "git", + "url": "https://github.com/OneSignal/onesignal-php-api.git", + "reference": "4da2d2aea68c1df8b460ad482fcfddbfd79549c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/OneSignal/onesignal-php-api/zipball/4da2d2aea68c1df8b460ad482fcfddbfd79549c9", + "reference": "4da2d2aea68c1df8b460ad482fcfddbfd79549c9", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "ext-mbstring": "*", + "guzzlehttp/guzzle": "^7.3", + "guzzlehttp/psr7": "^1.7 || ^2.0", + "php": "^7.3 || ^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "onesignal\\client\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "authors": [ + { + "name": "OneSignal developers", + "homepage": "https://onesignal.com" + } + ], + "description": "A powerful way to send personalized messages at scale and build effective customer engagement strategies. Learn more at onesignal.com", + "homepage": "https://onesignal.com", + "keywords": [ + "api", + "onesignal", + "php", + "rest", + "sdk" + ], + "support": { + "issues": "https://github.com/OneSignal/onesignal-php-api/issues", + "source": "https://github.com/OneSignal/onesignal-php-api/tree/v5.8.0" + }, + "time": "2026-06-24T04:17:13+00:00" + }, + { + "name": "paragonie/constant_time_encoding", + "version": "v3.1.3", + "source": { + "type": "git", + "url": "https://github.com/paragonie/constant_time_encoding.git", + "reference": "d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77", + "reference": "d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77", + "shasum": "" + }, + "require": { + "php": "^8" + }, + "require-dev": { + "infection/infection": "^0", + "nikic/php-fuzzer": "^0", + "phpunit/phpunit": "^9|^10|^11", + "vimeo/psalm": "^4|^5|^6" + }, + "type": "library", + "autoload": { + "psr-4": { + "ParagonIE\\ConstantTime\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com", + "role": "Maintainer" + }, + { + "name": "Steve 'Sc00bz' Thomas", + "email": "steve@tobtu.com", + "homepage": "https://www.tobtu.com", + "role": "Original Developer" + } + ], + "description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)", + "keywords": [ + "base16", + "base32", + "base32_decode", + "base32_encode", + "base64", + "base64_decode", + "base64_encode", + "bin2hex", + "encoding", + "hex", + "hex2bin", + "rfc4648" + ], + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/constant_time_encoding/issues", + "source": "https://github.com/paragonie/constant_time_encoding" + }, + "time": "2025-09-24T15:06:41+00:00" + }, + { + "name": "paragonie/random_compat", + "version": "v9.99.100", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a", + "shasum": "" + }, + "require": { + "php": ">= 7" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*", + "vimeo/psalm": "^1" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "polyfill", + "pseudorandom", + "random" + ], + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/random_compat/issues", + "source": "https://github.com/paragonie/random_compat" + }, + "time": "2020-10-15T08:29:30+00:00" + }, + { + "name": "phpoption/phpoption", + "version": "1.9.5", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/php-option.git", + "reference": "75365b91986c2405cf5e1e012c5595cd487a98be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/75365b91986c2405cf5e1e012c5595cd487a98be", + "reference": "75365b91986c2405cf5e1e012c5595cd487a98be", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.44 || ^9.6.25 || ^10.5.53 || ^11.5.34" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + }, + "branch-alias": { + "dev-master": "1.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpOption\\": "src/PhpOption/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "https://github.com/schmittjoh" + }, + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "Option Type for PHP", + "keywords": [ + "language", + "option", + "php", + "type" + ], + "support": { + "issues": "https://github.com/schmittjoh/php-option/issues", + "source": "https://github.com/schmittjoh/php-option/tree/1.9.5" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", + "type": "tidelift" + } + ], + "time": "2025-12-27T19:41:33+00:00" + }, + { + "name": "phpseclib/phpseclib", + "version": "3.0.55", + "source": { + "type": "git", + "url": "https://github.com/phpseclib/phpseclib.git", + "reference": "db9744e6d47e742b1f974e965ad49bdd041105af" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/db9744e6d47e742b1f974e965ad49bdd041105af", + "reference": "db9744e6d47e742b1f974e965ad49bdd041105af", + "shasum": "" + }, + "require": { + "paragonie/constant_time_encoding": "^1|^2|^3", + "paragonie/random_compat": "^1.4|^2.0|^9.99.99", + "php": ">=5.6.1" + }, + "require-dev": { + "phpunit/phpunit": "*" + }, + "suggest": { + "ext-dom": "Install the DOM extension to load XML formatted public keys.", + "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.", + "ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.", + "ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.", + "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations." + }, + "type": "library", + "autoload": { + "files": [ + "phpseclib/bootstrap.php" + ], + "psr-4": { + "phpseclib3\\": "phpseclib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jim Wigginton", + "email": "terrafrost@php.net", + "role": "Lead Developer" + }, + { + "name": "Patrick Monnerat", + "email": "pm@datasphere.ch", + "role": "Developer" + }, + { + "name": "Andreas Fischer", + "email": "bantu@phpbb.com", + "role": "Developer" + }, + { + "name": "Hans-Jürgen Petrich", + "email": "petrich@tronic-media.com", + "role": "Developer" + }, + { + "name": "Graham Campbell", + "email": "graham@alt-three.com", + "role": "Developer" + } + ], + "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.", + "homepage": "http://phpseclib.sourceforge.net", + "keywords": [ + "BigInteger", + "aes", + "asn.1", + "asn1", + "blowfish", + "crypto", + "cryptography", + "encryption", + "rsa", + "security", + "sftp", + "signature", + "signing", + "ssh", + "twofish", + "x.509", + "x509" + ], + "support": { + "issues": "https://github.com/phpseclib/phpseclib/issues", + "source": "https://github.com/phpseclib/phpseclib/tree/3.0.55" + }, + "funding": [ + { + "url": "https://github.com/terrafrost", + "type": "github" + }, + { + "url": "https://www.patreon.com/phpseclib", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib", + "type": "tidelift" + } + ], + "time": "2026-06-14T23:24:10+00:00" + }, + { + "name": "pragmarx/google2fa", + "version": "v9.0.0", + "source": { + "type": "git", + "url": "https://github.com/antonioribeiro/google2fa.git", + "reference": "e6bc62dd6ae83acc475f57912e27466019a1f2cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/antonioribeiro/google2fa/zipball/e6bc62dd6ae83acc475f57912e27466019a1f2cf", + "reference": "e6bc62dd6ae83acc475f57912e27466019a1f2cf", + "shasum": "" + }, + "require": { + "paragonie/constant_time_encoding": "^1.0|^2.0|^3.0", + "php": "^7.1|^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.9", + "phpunit/phpunit": "^7.5.15|^8.5|^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "PragmaRX\\Google2FA\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Antonio Carlos Ribeiro", + "email": "acr@antoniocarlosribeiro.com", + "role": "Creator & Designer" + } + ], + "description": "A One Time Password Authentication package, compatible with Google Authenticator.", + "keywords": [ + "2fa", + "Authentication", + "Two Factor Authentication", + "google2fa" + ], + "support": { + "issues": "https://github.com/antonioribeiro/google2fa/issues", + "source": "https://github.com/antonioribeiro/google2fa/tree/v9.0.0" + }, + "time": "2025-09-19T22:51:08+00:00" + }, + { + "name": "psr/clock", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/clock.git", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Clock\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for reading the clock.", + "homepage": "https://github.com/php-fig/clock", + "keywords": [ + "clock", + "now", + "psr", + "psr-20", + "time" + ], + "support": { + "issues": "https://github.com/php-fig/clock/issues", + "source": "https://github.com/php-fig/clock/tree/1.0.0" + }, + "time": "2022-11-25T14:36:26+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory" + }, + "time": "2024-04-15T12:06:14+00:00" + }, + { + "name": "psr/http-message", + "version": "2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+00:00" + }, + { + "name": "psr/log", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "time": "2024-09-11T13:17:53+00:00" + }, + { + "name": "psr/simple-cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" + }, + "time": "2021-10-29T13:26:27+00:00" + }, + { + "name": "psy/psysh", + "version": "v0.12.23", + "source": { + "type": "git", + "url": "https://github.com/bobthecow/psysh.git", + "reference": "4dcc0f08047d52bbde475eda481146fd8e27e1a4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/4dcc0f08047d52bbde475eda481146fd8e27e1a4", + "reference": "4dcc0f08047d52bbde475eda481146fd8e27e1a4", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-tokenizer": "*", + "nikic/php-parser": "^5.0 || ^4.0", + "php": "^8.0 || ^7.4", + "symfony/console": "^8.0 || ^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4", + "symfony/var-dumper": "^8.0 || ^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4" + }, + "conflict": { + "symfony/console": "4.4.37 || 5.3.14 || 5.3.15 || 5.4.3 || 5.4.4 || 6.0.3 || 6.0.4" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.2", + "composer/class-map-generator": "^1.6" + }, + "suggest": { + "composer/class-map-generator": "Improved tab completion performance with better class discovery.", + "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)", + "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well." + }, + "bin": [ + "bin/psysh" + ], + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": false, + "forward-command": false + }, + "branch-alias": { + "dev-main": "0.12.x-dev" + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Psy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Justin Hileman", + "email": "justin@justinhileman.info" + } + ], + "description": "An interactive shell for modern PHP.", + "homepage": "https://psysh.org", + "keywords": [ + "REPL", + "console", + "interactive", + "shell" + ], + "support": { + "issues": "https://github.com/bobthecow/psysh/issues", + "source": "https://github.com/bobthecow/psysh/tree/v0.12.23" + }, + "time": "2026-05-23T13:41:31+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "ramsey/collection", + "version": "2.1.1", + "source": { + "type": "git", + "url": "https://github.com/ramsey/collection.git", + "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/collection/zipball/344572933ad0181accbf4ba763e85a0306a8c5e2", + "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "captainhook/plugin-composer": "^5.3", + "ergebnis/composer-normalize": "^2.45", + "fakerphp/faker": "^1.24", + "hamcrest/hamcrest-php": "^2.0", + "jangregor/phpstan-prophecy": "^2.1", + "mockery/mockery": "^1.6", + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.4", + "phpspec/prophecy-phpunit": "^2.3", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-mockery": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^10.5", + "ramsey/coding-standard": "^2.3", + "ramsey/conventional-commits": "^1.6", + "roave/security-advisories": "dev-latest" + }, + "type": "library", + "extra": { + "captainhook": { + "force-install": true + }, + "ramsey/conventional-commits": { + "configFile": "conventional-commits.json" + } + }, + "autoload": { + "psr-4": { + "Ramsey\\Collection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ben Ramsey", + "email": "ben@benramsey.com", + "homepage": "https://benramsey.com" + } + ], + "description": "A PHP library for representing and manipulating collections.", + "keywords": [ + "array", + "collection", + "hash", + "map", + "queue", + "set" + ], + "support": { + "issues": "https://github.com/ramsey/collection/issues", + "source": "https://github.com/ramsey/collection/tree/2.1.1" + }, + "time": "2025-03-22T05:38:12+00:00" + }, + { + "name": "ramsey/uuid", + "version": "4.9.3", + "source": { + "type": "git", + "url": "https://github.com/ramsey/uuid.git", + "reference": "1df15849d00943a67d677dc9cfd80795f038c9f8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/1df15849d00943a67d677dc9cfd80795f038c9f8", + "reference": "1df15849d00943a67d677dc9cfd80795f038c9f8", + "shasum": "" + }, + "require": { + "brick/math": ">=0.8.16 <=0.18", + "php": "^8.0", + "ramsey/collection": "^1.2 || ^2.0" + }, + "replace": { + "rhumsaa/uuid": "self.version" + }, + "require-dev": { + "captainhook/captainhook": "^5.25", + "captainhook/plugin-composer": "^5.3", + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "ergebnis/composer-normalize": "^2.47", + "mockery/mockery": "^1.6", + "paragonie/random-lib": "^2", + "php-mock/php-mock": "^2.6", + "php-mock/php-mock-mockery": "^1.5", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "phpbench/phpbench": "^1.2.14", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-mockery": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^9.6", + "slevomat/coding-standard": "^8.18", + "squizlabs/php_codesniffer": "^3.13" + }, + "suggest": { + "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", + "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.", + "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.", + "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter", + "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." + }, + "type": "library", + "extra": { + "captainhook": { + "force-install": true + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Ramsey\\Uuid\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).", + "keywords": [ + "guid", + "identifier", + "uuid" + ], + "support": { + "issues": "https://github.com/ramsey/uuid/issues", + "source": "https://github.com/ramsey/uuid/tree/4.9.3" + }, + "time": "2026-06-18T03:57:49+00:00" + }, + { + "name": "symfony/clock", + "version": "v8.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/clock.git", + "reference": "701ef4de9705d6c32292ebee5e8044094a09fbf6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/clock/zipball/701ef4de9705d6c32292ebee5e8044094a09fbf6", + "reference": "701ef4de9705d6c32292ebee5e8044094a09fbf6", + "shasum": "" + }, + "require": { + "php": ">=8.4.1", + "psr/clock": "^1.0" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/now.php" + ], + "psr-4": { + "Symfony\\Component\\Clock\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Decouples applications from the system clock", + "homepage": "https://symfony.com", + "keywords": [ + "clock", + "psr20", + "time" + ], + "support": { + "source": "https://github.com/symfony/clock/tree/v8.1.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-29T05:06:50+00:00" + }, + { + "name": "symfony/console", + "version": "v8.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "f5a856c6ecb56b3c21ed94a5b7bf940d857d110a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/f5a856c6ecb56b3c21ed94a5b7bf940d857d110a", + "reference": "f5a856c6ecb56b3c21ed94a5b7bf940d857d110a", + "shasum": "" + }, + "require": { + "php": ">=8.4.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "^1.0", + "symfony/polyfill-php85": "^1.32", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^7.4.6|^8.0.6" + }, + "conflict": { + "symfony/dependency-injection": "<8.1", + "symfony/event-dispatcher": "<8.1" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^7.4|^8.0", + "symfony/dependency-injection": "^8.1", + "symfony/event-dispatcher": "^8.1", + "symfony/filesystem": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/lock": "^7.4|^8.0", + "symfony/messenger": "^7.4|^8.0", + "symfony/mime": "^7.4|^8.0", + "symfony/process": "^7.4|^8.0", + "symfony/stopwatch": "^7.4|^8.0", + "symfony/uid": "^7.4|^8.0", + "symfony/validator": "^7.4|^8.0", + "symfony/var-dumper": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v8.1.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-29T05:06:50+00:00" + }, + { + "name": "symfony/css-selector", + "version": "v8.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/css-selector.git", + "reference": "dc0e2be45c9b5588c82414f02ac574b4b986abcd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/dc0e2be45c9b5588c82414f02ac574b4b986abcd", + "reference": "dc0e2be45c9b5588c82414f02ac574b4b986abcd", + "shasum": "" + }, + "require": { + "php": ">=8.4.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\CssSelector\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Jean-François Simon", + "email": "jeanfrancois.simon@sensiolabs.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Converts CSS selectors to XPath expressions", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/css-selector/tree/v8.1.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-29T05:06:50+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.7.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/50f59d1f3ca46d41ac911f97a78626b6756af35b", + "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.7-dev" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-04-13T15:52:40+00:00" + }, + { + "name": "symfony/error-handler", + "version": "v8.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/error-handler.git", + "reference": "d8aeb1abd3fef84795567850d3a567bdb5945ee5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/d8aeb1abd3fef84795567850d3a567bdb5945ee5", + "reference": "d8aeb1abd3fef84795567850d3a567bdb5945ee5", + "shasum": "" + }, + "require": { + "php": ">=8.4.1", + "psr/log": "^1|^2|^3", + "symfony/polyfill-php85": "^1.32", + "symfony/var-dumper": "^7.4|^8.0" + }, + "conflict": { + "symfony/deprecation-contracts": "<2.5" + }, + "require-dev": { + "symfony/console": "^7.4|^8.0", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/serializer": "^7.4|^8.0", + "symfony/webpack-encore-bundle": "^1.0|^2.0" + }, + "bin": [ + "Resources/bin/patch-type-declarations" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\ErrorHandler\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to manage errors and ease debugging PHP code", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/error-handler/tree/v8.1.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-29T05:06:50+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v8.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "f249ae3f680958b6f1f9dd76e5747cf0695b4102" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/f249ae3f680958b6f1f9dd76e5747cf0695b4102", + "reference": "f249ae3f680958b6f1f9dd76e5747cf0695b4102", + "shasum": "" + }, + "require": { + "php": ">=8.4.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/event-dispatcher-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/security-http": "<7.4", + "symfony/service-contracts": "<2.5" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/error-handler": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/framework-bundle": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v8.1.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-29T05:06:50+00:00" + }, + { + "name": "symfony/event-dispatcher-contracts", + "version": "v3.7.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "ccba7060602b7fed0b03c85bf025257f76d9ef32" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/ccba7060602b7fed0b03c85bf025257f76d9ef32", + "reference": "ccba7060602b7fed0b03c85bf025257f76d9ef32", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/event-dispatcher": "^1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.7-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.7.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-01-05T13:30:16+00:00" + }, + { + "name": "symfony/finder", + "version": "v8.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "58d2e767a66052c1487356f953445634a8194c64" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/58d2e767a66052c1487356f953445634a8194c64", + "reference": "58d2e767a66052c1487356f953445634a8194c64", + "shasum": "" + }, + "require": { + "php": ">=8.4.1" + }, + "require-dev": { + "symfony/filesystem": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v8.1.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-29T05:06:50+00:00" + }, + { + "name": "symfony/http-foundation", + "version": "v8.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-foundation.git", + "reference": "af11474600f06718086c2cda4fa6fa8d0a672e7e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/af11474600f06718086c2cda4fa6fa8d0a672e7e", + "reference": "af11474600f06718086c2cda4fa6fa8d0a672e7e", + "shasum": "" + }, + "require": { + "php": ">=8.4.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "^1.1" + }, + "conflict": { + "doctrine/dbal": "<4.3" + }, + "require-dev": { + "doctrine/dbal": "^4.3", + "predis/predis": "^1.1|^2.0", + "symfony/cache": "^7.4|^8.0", + "symfony/clock": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/mime": "^7.4|^8.0", + "symfony/rate-limiter": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpFoundation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Defines an object-oriented layer for the HTTP specification", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-foundation/tree/v8.1.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-29T05:06:50+00:00" + }, + { + "name": "symfony/http-kernel", + "version": "v8.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-kernel.git", + "reference": "cefeb37c82eed3e0c42fa25ba64cd3a908d90f39" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/cefeb37c82eed3e0c42fa25ba64cd3a908d90f39", + "reference": "cefeb37c82eed3e0c42fa25ba64cd3a908d90f39", + "shasum": "" + }, + "require": { + "php": ">=8.4.1", + "psr/log": "^1|^2|^3", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/error-handler": "^7.4|^8.0", + "symfony/event-dispatcher": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/dependency-injection": "<8.1", + "symfony/flex": "<2.10", + "symfony/http-client-contracts": "<2.5", + "symfony/translation-contracts": "<2.5", + "symfony/var-dumper": "<8.1", + "symfony/web-profiler-bundle": "<8.1", + "twig/twig": "<3.21" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/cache": "^1.0|^2.0|^3.0", + "symfony/browser-kit": "^7.4|^8.0", + "symfony/clock": "^7.4|^8.0", + "symfony/config": "^7.4|^8.0", + "symfony/console": "^7.4|^8.0", + "symfony/css-selector": "^7.4|^8.0", + "symfony/dependency-injection": "^8.1", + "symfony/dom-crawler": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/finder": "^7.4|^8.0", + "symfony/http-client-contracts": "^2.5|^3", + "symfony/process": "^7.4|^8.0", + "symfony/property-access": "^7.4|^8.0", + "symfony/rate-limiter": "^7.4|^8.0", + "symfony/routing": "^7.4|^8.0", + "symfony/serializer": "^7.4|^8.0", + "symfony/stopwatch": "^7.4|^8.0", + "symfony/translation": "^7.4|^8.0", + "symfony/translation-contracts": "^2.5|^3", + "symfony/uid": "^7.4|^8.0", + "symfony/validator": "^7.4|^8.0", + "symfony/var-dumper": "^8.1", + "symfony/var-exporter": "^7.4|^8.0", + "twig/twig": "^3.21" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpKernel\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a structured process for converting a Request into a Response", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-kernel/tree/v8.1.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-29T08:46:08+00:00" + }, + { + "name": "symfony/mailer", + "version": "v8.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/mailer.git", + "reference": "9418d772df3a03a142e3bc06f602adb2b8724877" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mailer/zipball/9418d772df3a03a142e3bc06f602adb2b8724877", + "reference": "9418d772df3a03a142e3bc06f602adb2b8724877", + "shasum": "" + }, + "require": { + "egulias/email-validator": "^2.1.10|^3|^4", + "php": ">=8.4.1", + "psr/event-dispatcher": "^1", + "psr/log": "^1|^2|^3", + "symfony/event-dispatcher": "^7.4|^8.0", + "symfony/mime": "^7.4|^8.0", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/http-client-contracts": "<2.5" + }, + "require-dev": { + "symfony/console": "^7.4|^8.0", + "symfony/http-client": "^7.4|^8.0", + "symfony/messenger": "^7.4|^8.0", + "symfony/twig-bridge": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mailer\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Helps sending emails", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/mailer/tree/v8.1.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-29T05:06:50+00:00" + }, + { + "name": "symfony/mime", + "version": "v8.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/mime.git", + "reference": "b164ae7e3f7915aacfe9ee155f2f358502440664" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mime/zipball/b164ae7e3f7915aacfe9ee155f2f358502440664", + "reference": "b164ae7e3f7915aacfe9ee155f2f358502440664", + "shasum": "" + }, + "require": { + "php": ">=8.4.1", + "symfony/polyfill-intl-idn": "^1.10", + "symfony/polyfill-mbstring": "^1.0" + }, + "conflict": { + "egulias/email-validator": "~3.0.0", + "phpdocumentor/reflection-docblock": "<5.2|>=7", + "phpdocumentor/type-resolver": "<1.5.1" + }, + "require-dev": { + "egulias/email-validator": "^2.1.10|^3.1|^4", + "league/html-to-markdown": "^5.0", + "phpdocumentor/reflection-docblock": "^5.2|^6.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/process": "^7.4|^8.0", + "symfony/property-access": "^7.4|^8.0", + "symfony/property-info": "^7.4|^8.0", + "symfony/serializer": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mime\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows manipulating MIME messages", + "homepage": "https://symfony.com", + "keywords": [ + "mime", + "mime-type" + ], + "support": { + "source": "https://github.com/symfony/mime/tree/v8.1.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-29T05:06:50+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.37.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "141046a8f9477948ff284fa65be2095baafb94f2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/141046a8f9477948ff284fa65be2095baafb94f2", + "reference": "141046a8f9477948ff284fa65be2095baafb94f2", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.37.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-04-10T16:19:22+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.38.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "e9247d281d694a5120554d9afaf54e070e88a603" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/e9247d281d694a5120554d9afaf54e070e88a603", + "reference": "e9247d281d694a5120554d9afaf54e070e88a603", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.38.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-26T05:58:03+00:00" + }, + { + "name": "symfony/polyfill-intl-idn", + "version": "v1.38.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "dc21118016c039a66235cf93d96b435ffb282412" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/dc21118016c039a66235cf93d96b435ffb282412", + "reference": "dc21118016c039a66235cf93d96b435ffb282412", + "shasum": "" + }, + "require": { + "php": ">=7.2", + "symfony/polyfill-intl-normalizer": "^1.10" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Idn\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Laurent Bassin", + "email": "laurent@bassin.info" + }, + { + "name": "Trevor Rowbotham", + "email": "trevor.rowbotham@pm.me" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "idn", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.38.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-25T15:22:23+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.38.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/2d446c214bdbe5b71bde5011b060a05fece3ae6b", + "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.38.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-25T13:48:31+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.38.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6", + "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6", + "shasum": "" + }, + "require": { + "ext-iconv": "*", + "php": ">=7.2" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.38.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-27T06:59:30+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.37.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dfb55726c3a76ea3b6459fcfda1ec2d80a682411", + "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.37.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-04-10T16:19:22+00:00" + }, + { + "name": "symfony/polyfill-php84", + "version": "v1.38.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php84.git", + "reference": "f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa", + "reference": "f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php84\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php84/tree/v1.38.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-26T12:51:13+00:00" + }, + { + "name": "symfony/polyfill-php85", + "version": "v1.38.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php85.git", + "reference": "ba2ba04f3352cfa2dcbbcb90aee13ed967f505b1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/ba2ba04f3352cfa2dcbbcb90aee13ed967f505b1", + "reference": "ba2ba04f3352cfa2dcbbcb90aee13ed967f505b1", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php85\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.5+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php85/tree/v1.38.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-26T02:25:22+00:00" + }, + { + "name": "symfony/polyfill-php86", + "version": "v1.38.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php86.git", + "reference": "fcec68d64f46dc84e1f6ffcf2c6dda40ff3143ad" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php86/zipball/fcec68d64f46dc84e1f6ffcf2c6dda40ff3143ad", + "reference": "fcec68d64f46dc84e1f6ffcf2c6dda40ff3143ad", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php86\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.6+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php86/tree/v1.38.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-25T11:52:35+00:00" + }, + { + "name": "symfony/polyfill-uuid", + "version": "v1.37.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-uuid.git", + "reference": "26dfec253c4cf3e51b541b52ddf7e42cb0908e94" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/26dfec253c4cf3e51b541b52ddf7e42cb0908e94", + "reference": "26dfec253c4cf3e51b541b52ddf7e42cb0908e94", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-uuid": "*" + }, + "suggest": { + "ext-uuid": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Uuid\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for uuid functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "uuid" + ], + "support": { + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.37.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-04-10T16:19:22+00:00" + }, + { + "name": "symfony/process", + "version": "v8.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "c4a9e58f235a6bf7f97ffbfedae2687353ac79e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/c4a9e58f235a6bf7f97ffbfedae2687353ac79e5", + "reference": "c4a9e58f235a6bf7f97ffbfedae2687353ac79e5", + "shasum": "" + }, + "require": { + "php": ">=8.4.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v8.1.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-29T05:06:50+00:00" + }, + { + "name": "symfony/routing", + "version": "v8.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/routing.git", + "reference": "fe0bfec72c8a806109fb9c3a5f2b898fe0c76eb3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/routing/zipball/fe0bfec72c8a806109fb9c3a5f2b898fe0c76eb3", + "reference": "fe0bfec72c8a806109fb9c3a5f2b898fe0c76eb3", + "shasum": "" + }, + "require": { + "php": ">=8.4.1", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/yaml": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Routing\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Maps an HTTP request to a set of configuration variables", + "homepage": "https://symfony.com", + "keywords": [ + "router", + "routing", + "uri", + "url" + ], + "support": { + "source": "https://github.com/symfony/routing/tree/v8.1.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-29T05:06:50+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.7.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d25d82433a80eba6aa0e6c24b61d7370d99e444a", + "reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.7-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.7.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-03-28T09:44:51+00:00" + }, + { + "name": "symfony/string", + "version": "v8.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "afd5944f4005862d961efb85c8bbd5c523c4e3c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/afd5944f4005862d961efb85c8bbd5c523c4e3c9", + "reference": "afd5944f4005862d961efb85c8bbd5c523c4e3c9", + "shasum": "" + }, + "require": { + "php": ">=8.4.1", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-intl-grapheme": "^1.33", + "symfony/polyfill-intl-normalizer": "^1.0", + "symfony/polyfill-mbstring": "^1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "require-dev": { + "symfony/emoji": "^7.4|^8.0", + "symfony/http-client": "^7.4|^8.0", + "symfony/intl": "^7.4|^8.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v8.1.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-29T05:06:50+00:00" + }, + { + "name": "symfony/translation", + "version": "v8.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation.git", + "reference": "b2bd012ca28c4acae830ee1206a5b6e35dd99693" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation/zipball/b2bd012ca28c4acae830ee1206a5b6e35dd99693", + "reference": "b2bd012ca28c4acae830ee1206a5b6e35dd99693", + "shasum": "" + }, + "require": { + "php": ">=8.4.1", + "symfony/polyfill-mbstring": "^1.0", + "symfony/translation-contracts": "^3.6.1" + }, + "conflict": { + "nikic/php-parser": "<5.0", + "symfony/http-client-contracts": "<2.5", + "symfony/service-contracts": "<2.5" + }, + "provide": { + "symfony/translation-implementation": "2.3|3.0" + }, + "require-dev": { + "nikic/php-parser": "^5.0", + "psr/log": "^1|^2|^3", + "symfony/config": "^7.4|^8.0", + "symfony/console": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/finder": "^7.4|^8.0", + "symfony/http-client-contracts": "^2.5|^3.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/intl": "^7.4|^8.0", + "symfony/polyfill-intl-icu": "^1.21", + "symfony/routing": "^7.4|^8.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to internationalize your application", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/translation/tree/v8.1.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-29T05:06:50+00:00" + }, + { + "name": "symfony/translation-contracts", + "version": "v3.7.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "0ab302977a952b42fd51475c4ebac81f8da0a95d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/0ab302977a952b42fd51475c4ebac81f8da0a95d", + "reference": "0ab302977a952b42fd51475c4ebac81f8da0a95d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.7-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to translation", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/translation-contracts/tree/v3.7.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-01-05T13:30:16+00:00" + }, + { + "name": "symfony/uid", + "version": "v8.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/uid.git", + "reference": "7393f157a55f7e70a4de0334435c55a5a8fe749a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/uid/zipball/7393f157a55f7e70a4de0334435c55a5a8fe749a", + "reference": "7393f157a55f7e70a4de0334435c55a5a8fe749a", + "shasum": "" + }, + "require": { + "php": ">=8.4.1", + "symfony/polyfill-uuid": "^1.15" + }, + "require-dev": { + "symfony/console": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Uid\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to generate and represent UIDs", + "homepage": "https://symfony.com", + "keywords": [ + "UID", + "ulid", + "uuid" + ], + "support": { + "source": "https://github.com/symfony/uid/tree/v8.1.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-29T05:06:50+00:00" + }, + { + "name": "symfony/var-dumper", + "version": "v8.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "c2c4df1d21477cc21c9f6dc1b14d07c3abc4963e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/c2c4df1d21477cc21c9f6dc1b14d07c3abc4963e", + "reference": "c2c4df1d21477cc21c9f6dc1b14d07c3abc4963e", + "shasum": "" + }, + "require": { + "php": ">=8.4.1", + "symfony/polyfill-mbstring": "^1.0" + }, + "conflict": { + "symfony/console": "<7.4", + "symfony/error-handler": "<7.4" + }, + "require-dev": { + "symfony/console": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/process": "^7.4|^8.0", + "symfony/uid": "^7.4|^8.0", + "twig/twig": "^3.12" + }, + "bin": [ + "Resources/bin/var-dump-server" + ], + "type": "library", + "autoload": { + "files": [ + "Resources/functions/dump.php" + ], + "psr-4": { + "Symfony\\Component\\VarDumper\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides mechanisms for walking through any arbitrary PHP variable", + "homepage": "https://symfony.com", + "keywords": [ + "debug", + "dump" + ], + "support": { + "source": "https://github.com/symfony/var-dumper/tree/v8.1.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-29T05:06:50+00:00" + }, + { + "name": "tijsverkoyen/css-to-inline-styles", + "version": "v2.4.0", + "source": { + "type": "git", + "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git", + "reference": "f0292ccf0ec75843d65027214426b6b163b48b41" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/f0292ccf0ec75843d65027214426b6b163b48b41", + "reference": "f0292ccf0ec75843d65027214426b6b163b48b41", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "php": "^7.4 || ^8.0", + "symfony/css-selector": "^5.4 || ^6.0 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^8.5.21 || ^9.5.10" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "TijsVerkoyen\\CssToInlineStyles\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Tijs Verkoyen", + "email": "css_to_inline_styles@verkoyen.eu", + "role": "Developer" + } + ], + "description": "CssToInlineStyles is a class that enables you to convert HTML-pages/files into HTML-pages/files with inline styles. This is very useful when you're sending emails.", + "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles", + "support": { + "issues": "https://github.com/tijsverkoyen/CssToInlineStyles/issues", + "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/v2.4.0" + }, + "time": "2025-12-02T11:56:42+00:00" + }, + { + "name": "vlucas/phpdotenv", + "version": "v5.6.3", + "source": { + "type": "git", + "url": "https://github.com/vlucas/phpdotenv.git", + "reference": "955e7815d677a3eaa7075231212f2110983adecc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/955e7815d677a3eaa7075231212f2110983adecc", + "reference": "955e7815d677a3eaa7075231212f2110983adecc", + "shasum": "" + }, + "require": { + "ext-pcre": "*", + "graham-campbell/result-type": "^1.1.4", + "php": "^7.2.5 || ^8.0", + "phpoption/phpoption": "^1.9.5", + "symfony/polyfill-ctype": "^1.26", + "symfony/polyfill-mbstring": "^1.26", + "symfony/polyfill-php80": "^1.26" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-filter": "*", + "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2" + }, + "suggest": { + "ext-filter": "Required to use the boolean validator." + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + }, + "branch-alias": { + "dev-master": "5.6-dev" + } + }, + "autoload": { + "psr-4": { + "Dotenv\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Vance Lucas", + "email": "vance@vancelucas.com", + "homepage": "https://github.com/vlucas" + } + ], + "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", + "keywords": [ + "dotenv", + "env", + "environment" + ], + "support": { + "issues": "https://github.com/vlucas/phpdotenv/issues", + "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv", + "type": "tidelift" + } + ], + "time": "2025-12-27T19:49:13+00:00" + }, + { + "name": "voku/portable-ascii", + "version": "2.1.1", + "source": { + "type": "git", + "url": "https://github.com/voku/portable-ascii.git", + "reference": "8e1051fe39379367aecf014f41744ce7539a856f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/voku/portable-ascii/zipball/8e1051fe39379367aecf014f41744ce7539a856f", + "reference": "8e1051fe39379367aecf014f41744ce7539a856f", + "shasum": "" + }, + "require": { + "php": ">=7.1.0" + }, + "require-dev": { + "phpunit/phpunit": "~8.5 || ~9.6 || ~10.5 || ~11.5" + }, + "suggest": { + "ext-intl": "Use Intl for transliterator_transliterate() support" + }, + "type": "library", + "autoload": { + "psr-4": { + "voku\\": "src/voku/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Lars Moelleken", + "homepage": "https://www.moelleken.org/" + } + ], + "description": "Portable ASCII library - performance optimized (ascii) string functions for php.", + "homepage": "https://github.com/voku/portable-ascii", + "keywords": [ + "ascii", + "clean", + "php" + ], + "support": { + "issues": "https://github.com/voku/portable-ascii/issues", + "source": "https://github.com/voku/portable-ascii/tree/2.1.1" + }, + "funding": [ + { + "url": "https://www.paypal.me/moelleken", + "type": "custom" + }, + { + "url": "https://github.com/voku", + "type": "github" + }, + { + "url": "https://opencollective.com/portable-ascii", + "type": "open_collective" + }, + { + "url": "https://www.patreon.com/voku", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii", + "type": "tidelift" + } + ], + "time": "2026-04-26T05:33:54+00:00" + } + ], + "packages-dev": [ + { + "name": "fakerphp/faker", + "version": "v1.24.1", + "source": { + "type": "git", + "url": "https://github.com/FakerPHP/Faker.git", + "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", + "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "psr/container": "^1.0 || ^2.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "conflict": { + "fzaninotto/faker": "*" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "doctrine/persistence": "^1.3 || ^2.0", + "ext-intl": "*", + "phpunit/phpunit": "^9.5.26", + "symfony/phpunit-bridge": "^5.4.16" + }, + "suggest": { + "doctrine/orm": "Required to use Faker\\ORM\\Doctrine", + "ext-curl": "Required by Faker\\Provider\\Image to download images.", + "ext-dom": "Required by Faker\\Provider\\HtmlLorem for generating random HTML.", + "ext-iconv": "Required by Faker\\Provider\\ru_RU\\Text::realText() for generating real Russian text.", + "ext-mbstring": "Required for multibyte Unicode string functionality." + }, + "type": "library", + "autoload": { + "psr-4": { + "Faker\\": "src/Faker/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "François Zaninotto" + } + ], + "description": "Faker is a PHP library that generates fake data for you.", + "keywords": [ + "data", + "faker", + "fixtures" + ], + "support": { + "issues": "https://github.com/FakerPHP/Faker/issues", + "source": "https://github.com/FakerPHP/Faker/tree/v1.24.1" + }, + "time": "2024-11-21T13:46:39+00:00" + }, + { + "name": "filp/whoops", + "version": "2.18.4", + "source": { + "type": "git", + "url": "https://github.com/filp/whoops.git", + "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/filp/whoops/zipball/d2102955e48b9fd9ab24280a7ad12ed552752c4d", + "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0", + "psr/log": "^1.0.1 || ^2.0 || ^3.0" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.3.3", + "symfony/var-dumper": "^4.0 || ^5.0" + }, + "suggest": { + "symfony/var-dumper": "Pretty print complex values better with var-dumper available", + "whoops/soap": "Formats errors as SOAP responses" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Whoops\\": "src/Whoops/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Filipe Dobreira", + "homepage": "https://github.com/filp", + "role": "Developer" + } + ], + "description": "php error handling for cool kids", + "homepage": "https://filp.github.io/whoops/", + "keywords": [ + "error", + "exception", + "handling", + "library", + "throwable", + "whoops" + ], + "support": { + "issues": "https://github.com/filp/whoops/issues", + "source": "https://github.com/filp/whoops/tree/2.18.4" + }, + "funding": [ + { + "url": "https://github.com/denis-sokolov", + "type": "github" + } + ], + "time": "2025-08-08T12:00:00+00:00" + }, + { + "name": "hamcrest/hamcrest-php", + "version": "v2.1.1", + "source": { + "type": "git", + "url": "https://github.com/hamcrest/hamcrest-php.git", + "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", + "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0" + }, + "replace": { + "cordoval/hamcrest-php": "*", + "davedevelopment/hamcrest-php": "*", + "kodova/hamcrest-php": "*" + }, + "require-dev": { + "phpunit/php-file-iterator": "^1.4 || ^2.0 || ^3.0", + "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + }, + "autoload": { + "classmap": [ + "hamcrest" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "This is the PHP port of Hamcrest Matchers", + "keywords": [ + "test" + ], + "support": { + "issues": "https://github.com/hamcrest/hamcrest-php/issues", + "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.1.1" + }, + "time": "2025-04-30T06:54:44+00:00" + }, + { + "name": "laravel/agent-detector", + "version": "v2.0.2", + "source": { + "type": "git", + "url": "https://github.com/laravel/agent-detector.git", + "reference": "90694b9256099591cf9e55d08c18ba7a00bf099f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/agent-detector/zipball/90694b9256099591cf9e55d08c18ba7a00bf099f", + "reference": "90694b9256099591cf9e55d08c18ba7a00bf099f", + "shasum": "" + }, + "require": { + "php": "^8.2.0" + }, + "require-dev": { + "laravel/pint": "^1.24.0", + "pestphp/pest": "^3.8.5|^4.1.0", + "pestphp/pest-plugin-type-coverage": "^3.0|^4.0.2", + "phpstan/phpstan": "^2.1.26", + "rector/rector": "^2.1.7", + "symfony/var-dumper": "^7.3.3" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Laravel\\AgentDetector\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Detect if code is running in an AI agent or automated development environment", + "homepage": "https://github.com/laravel/agent-detector", + "keywords": [ + "Agent", + "ai", + "automation", + "claude", + "cursor", + "detection", + "devin", + "php" + ], + "support": { + "issues": "https://github.com/laravel/agent-detector/issues", + "source": "https://github.com/laravel/agent-detector" + }, + "time": "2026-04-29T18:32:34+00:00" + }, + { + "name": "laravel/pail", + "version": "v1.2.7", + "source": { + "type": "git", + "url": "https://github.com/laravel/pail.git", + "reference": "2f7d27dada8effc48b8c424445a69cca7007daaa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/pail/zipball/2f7d27dada8effc48b8c424445a69cca7007daaa", + "reference": "2f7d27dada8effc48b8c424445a69cca7007daaa", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "illuminate/console": "^10.24|^11.0|^12.0|^13.0", + "illuminate/contracts": "^10.24|^11.0|^12.0|^13.0", + "illuminate/log": "^10.24|^11.0|^12.0|^13.0", + "illuminate/process": "^10.24|^11.0|^12.0|^13.0", + "illuminate/support": "^10.24|^11.0|^12.0|^13.0", + "nunomaduro/termwind": "^1.15|^2.0", + "php": "^8.2", + "symfony/console": "^6.0|^7.0|^8.0" + }, + "require-dev": { + "laravel/framework": "^10.24|^11.0|^12.0|^13.0", + "laravel/pint": "^1.13", + "orchestra/testbench-core": "^8.13|^9.17|^10.8|^11.0", + "pestphp/pest": "^2.20|^3.0|^4.0", + "pestphp/pest-plugin-type-coverage": "^2.3|^3.0|^4.0", + "phpstan/phpstan": "^1.12.27", + "symfony/var-dumper": "^6.3|^7.0|^8.0", + "symfony/yaml": "^6.3|^7.0|^8.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Pail\\PailServiceProvider" + ] + }, + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\Pail\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Easily delve into your Laravel application's log files directly from the command line.", + "homepage": "https://github.com/laravel/pail", + "keywords": [ + "dev", + "laravel", + "logs", + "php", + "tail" + ], + "support": { + "issues": "https://github.com/laravel/pail/issues", + "source": "https://github.com/laravel/pail" + }, + "time": "2026-05-20T22:24:57+00:00" + }, + { + "name": "laravel/pao", + "version": "v1.1.2", + "source": { + "type": "git", + "url": "https://github.com/laravel/pao.git", + "reference": "41b3c61ebeddce52a446afe6d21e0b02983fb2f6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/pao/zipball/41b3c61ebeddce52a446afe6d21e0b02983fb2f6", + "reference": "41b3c61ebeddce52a446afe6d21e0b02983fb2f6", + "shasum": "" + }, + "require": { + "laravel/agent-detector": "^2.0.2", + "php": "^8.3" + }, + "conflict": { + "laravel/framework": "<12.0.0", + "nunomaduro/collision": "<8.9.3", + "pestphp/pest": "<4.6.3 || >=6.0.0", + "phpunit/phpunit": "<12.5.23 || >=13.0.0 <13.1.7 || >=14.0.0" + }, + "require-dev": { + "brianium/paratest": "^7.20.0", + "laravel/pint": "^1.29.1", + "orchestra/testbench": "^10.11.0 || ^11.1.0", + "pestphp/pest": "^4.7.2 || ^5.0.0", + "pestphp/pest-plugin-type-coverage": "^4.0.4 || ^5.0.0", + "phpstan/phpstan": "^2.2.2", + "rector/rector": "^2.4.5", + "symfony/process": "^7.4.8 || ^8.1.0", + "symfony/var-dumper": "^7.4.8 || ^8.1.0" + }, + "type": "library", + "extra": { + "pest": { + "plugins": [ + "Laravel\\Pao\\Drivers\\Pest\\Plugin" + ] + }, + "laravel": { + "providers": [ + "Laravel\\Pao\\Laravel\\ServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "src/Autoload.php" + ], + "psr-4": { + "Laravel\\Pao\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Agent-optimized output for PHP testing tools", + "keywords": [ + "Agent", + "PHPStan", + "ai", + "dev", + "paratest", + "pest", + "php", + "phpunit", + "rector", + "testing" + ], + "support": { + "issues": "https://github.com/laravel/pao/issues", + "source": "https://github.com/laravel/pao" + }, + "time": "2026-06-22T19:58:00+00:00" + }, + { + "name": "laravel/pint", + "version": "v1.29.3", + "source": { + "type": "git", + "url": "https://github.com/laravel/pint.git", + "reference": "da1d1111a6aa2e082d2a388b194afe1ba0a05d14" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/pint/zipball/da1d1111a6aa2e082d2a388b194afe1ba0a05d14", + "reference": "da1d1111a6aa2e082d2a388b194afe1ba0a05d14", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "ext-tokenizer": "*", + "ext-xml": "*", + "php": "^8.2.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.95.8", + "illuminate/view": "^12.62.0", + "larastan/larastan": "^3.10.0", + "laravel-zero/framework": "^12.1.0", + "laravel/agent-detector": "^2.0.2", + "mockery/mockery": "^1.6.12", + "nunomaduro/termwind": "^2.4.0", + "pestphp/pest": "^3.8.6" + }, + "bin": [ + "builds/pint" + ], + "type": "project", + "autoload": { + "psr-4": { + "App\\": "app/", + "Database\\Seeders\\": "database/seeders/", + "Database\\Factories\\": "database/factories/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "An opinionated code formatter for PHP.", + "homepage": "https://laravel.com", + "keywords": [ + "dev", + "format", + "formatter", + "lint", + "linter", + "php" + ], + "support": { + "issues": "https://github.com/laravel/pint/issues", + "source": "https://github.com/laravel/pint" + }, + "time": "2026-06-16T15:34:04+00:00" + }, + { + "name": "mockery/mockery", + "version": "1.6.12", + "source": { + "type": "git", + "url": "https://github.com/mockery/mockery.git", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mockery/mockery/zipball/1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "shasum": "" + }, + "require": { + "hamcrest/hamcrest-php": "^2.0.1", + "lib-pcre": ">=7.0", + "php": ">=7.3" + }, + "conflict": { + "phpunit/phpunit": "<8.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.5 || ^9.6.17", + "symplify/easy-coding-standard": "^12.1.14" + }, + "type": "library", + "autoload": { + "files": [ + "library/helpers.php", + "library/Mockery.php" + ], + "psr-4": { + "Mockery\\": "library/Mockery" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Pádraic Brady", + "email": "padraic.brady@gmail.com", + "homepage": "https://github.com/padraic", + "role": "Author" + }, + { + "name": "Dave Marshall", + "email": "dave.marshall@atstsolutions.co.uk", + "homepage": "https://davedevelopment.co.uk", + "role": "Developer" + }, + { + "name": "Nathanael Esayeas", + "email": "nathanael.esayeas@protonmail.com", + "homepage": "https://github.com/ghostwriter", + "role": "Lead Developer" + } + ], + "description": "Mockery is a simple yet flexible PHP mock object framework", + "homepage": "https://github.com/mockery/mockery", + "keywords": [ + "BDD", + "TDD", + "library", + "mock", + "mock objects", + "mockery", + "stub", + "test", + "test double", + "testing" + ], + "support": { + "docs": "https://docs.mockery.io/", + "issues": "https://github.com/mockery/mockery/issues", + "rss": "https://github.com/mockery/mockery/releases.atom", + "security": "https://github.com/mockery/mockery/security/advisories", + "source": "https://github.com/mockery/mockery" + }, + "time": "2024-05-16T03:13:13+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.13.4", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2025-08-01T08:46:24+00:00" + }, + { + "name": "nunomaduro/collision", + "version": "v8.9.4", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/collision.git", + "reference": "716af8f95a470e9094cfca09ed897b023be191a5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/716af8f95a470e9094cfca09ed897b023be191a5", + "reference": "716af8f95a470e9094cfca09ed897b023be191a5", + "shasum": "" + }, + "require": { + "filp/whoops": "^2.18.4", + "nunomaduro/termwind": "^2.4.0", + "php": "^8.2.0", + "symfony/console": "^7.4.8 || ^8.0.8" + }, + "conflict": { + "laravel/framework": "<11.48.0 || >=14.0.0", + "phpunit/phpunit": "<11.5.50 || >=14.0.0" + }, + "require-dev": { + "brianium/paratest": "^7.8.5", + "larastan/larastan": "^3.9.6", + "laravel/framework": "^11.48.0 || ^12.56.0 || ^13.5.0", + "laravel/pint": "^1.29.1", + "orchestra/testbench-core": "^9.12.0 || ^10.12.1 || ^11.2.1", + "pestphp/pest": "^3.8.5 || ^4.4.3 || ^5.0.0", + "sebastian/environment": "^7.2.1 || ^8.0.4 || ^9.3.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider" + ] + }, + "branch-alias": { + "dev-8.x": "8.x-dev" + } + }, + "autoload": { + "files": [ + "./src/Adapters/Phpunit/Autoload.php" + ], + "psr-4": { + "NunoMaduro\\Collision\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Cli error handling for console/command-line PHP applications.", + "keywords": [ + "artisan", + "cli", + "command-line", + "console", + "dev", + "error", + "handling", + "laravel", + "laravel-zero", + "php", + "symfony" + ], + "support": { + "issues": "https://github.com/nunomaduro/collision/issues", + "source": "https://github.com/nunomaduro/collision" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://www.patreon.com/nunomaduro", + "type": "patreon" + } + ], + "time": "2026-04-21T14:04:20+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "12.5.7", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "186dab580576598076de6818596d12b61801880e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/186dab580576598076de6818596d12b61801880e", + "reference": "186dab580576598076de6818596d12b61801880e", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^5.7.0", + "php": ">=8.3", + "phpunit/php-text-template": "^5.0", + "sebastian/complexity": "^5.0", + "sebastian/environment": "^8.1.2", + "sebastian/lines-of-code": "^4.0.1", + "sebastian/version": "^6.0", + "theseer/tokenizer": "^2.0.1" + }, + "require-dev": { + "phpunit/phpunit": "^12.5.28" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "12.5.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.5.7" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage", + "type": "tidelift" + } + ], + "time": "2026-06-01T13:24:19+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "6.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "3d1cd096ef6bea4bf2762ba586e35dbd317cbfd5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3d1cd096ef6bea4bf2762ba586e35dbd317cbfd5", + "reference": "3d1cd096ef6bea4bf2762ba586e35dbd317cbfd5", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/6.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-file-iterator", + "type": "tidelift" + } + ], + "time": "2026-02-02T14:04:18+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/12b54e689b07a25a9b41e57736dfab6ec9ae5406", + "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^12.0" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:58:58+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/e1367a453f0eda562eedb4f659e13aa900d66c53", + "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:59:16+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "8.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc", + "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "8.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "security": "https://github.com/sebastianbergmann/php-timer/security/policy", + "source": "https://github.com/sebastianbergmann/php-timer/tree/8.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:59:38+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "12.5.30", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "900400a5b616d6fb306f9549f6da33ba615d3fbb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/900400a5b616d6fb306f9549f6da33ba615d3fbb", + "reference": "900400a5b616d6fb306f9549f6da33ba615d3fbb", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.13.4", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=8.3", + "phpunit/php-code-coverage": "^12.5.7", + "phpunit/php-file-iterator": "^6.0.1", + "phpunit/php-invoker": "^6.0.0", + "phpunit/php-text-template": "^5.0.0", + "phpunit/php-timer": "^8.0.0", + "sebastian/cli-parser": "^4.2.1", + "sebastian/comparator": "^7.1.8", + "sebastian/diff": "^7.0.0", + "sebastian/environment": "^8.1.2", + "sebastian/exporter": "^7.0.3", + "sebastian/global-state": "^8.0.3", + "sebastian/object-enumerator": "^7.0.0", + "sebastian/recursion-context": "^7.0.1", + "sebastian/type": "^6.0.4", + "sebastian/version": "^6.0.0", + "staabm/side-effects-detector": "^1.0.5" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "12.5-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.30" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsoring.html", + "type": "other" + } + ], + "time": "2026-06-15T13:12:30+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "4.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "7d05781b13f7dec9043a629a21d086ed74582a15" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/7d05781b13f7dec9043a629a21d086ed74582a15", + "reference": "7d05781b13f7dec9043a629a21d086ed74582a15", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.5.25" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/4.2.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/cli-parser", + "type": "tidelift" + } + ], + "time": "2026-05-17T05:29:34+00:00" + }, + { + "name": "sebastian/comparator", + "version": "7.1.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "7c65c1e79836812819705b473a90c12399542485" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/7c65c1e79836812819705b473a90c12399542485", + "reference": "7c65c1e79836812819705b473a90c12399542485", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.3", + "sebastian/diff": "^7.0", + "sebastian/exporter": "^7.0.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.5.25" + }, + "suggest": { + "ext-bcmath": "For comparing BcMath\\Number objects" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/7.1.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" + } + ], + "time": "2026-05-21T04:45:25+00:00" + }, + { + "name": "sebastian/complexity", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/bad4316aba5303d0221f43f8cee37eb58d384bbb", + "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.0", + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:55:25+00:00" + }, + { + "name": "sebastian/diff", + "version": "7.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "7ab1ea946c012266ca32390913653d844ecd085f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7ab1ea946c012266ca32390913653d844ecd085f", + "reference": "7ab1ea946c012266ca32390913653d844ecd085f", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0", + "symfony/process": "^7.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/7.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:55:46+00:00" + }, + { + "name": "sebastian/environment", + "version": "8.1.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "9d32c685773823b1983e256ae4ecd48a10d6e439" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/9d32c685773823b1983e256ae4ecd48a10d6e439", + "reference": "9d32c685773823b1983e256ae4ecd48a10d6e439", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.5.26" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "8.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "https://github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/8.1.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/environment", + "type": "tidelift" + } + ], + "time": "2026-05-25T13:40:20+00:00" + }, + { + "name": "sebastian/exporter", + "version": "7.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "c5e21b5de653ce0a769fb36f5cdfcb5e7a32cf23" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/c5e21b5de653ce0a769fb36f5cdfcb5e7a32cf23", + "reference": "c5e21b5de653ce0a769fb36f5cdfcb5e7a32cf23", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=8.3", + "sebastian/recursion-context": "^7.0.1" + }, + "require-dev": { + "phpunit/phpunit": "^12.5.25" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/7.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" + } + ], + "time": "2026-05-20T04:37:17+00:00" + }, + { + "name": "sebastian/global-state", + "version": "8.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "b164d3274d6537ab462591c5755f76a8f5b1aae9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/b164d3274d6537ab462591c5755f76a8f5b1aae9", + "reference": "b164d3274d6537ab462591c5755f76a8f5b1aae9", + "shasum": "" + }, + "require": { + "php": ">=8.3", + "sebastian/object-reflector": "^5.0", + "sebastian/recursion-context": "^7.0.1" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^12.5.28" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "8.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/8.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/global-state", + "type": "tidelift" + } + ], + "time": "2026-06-01T15:10:33+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "d543b8ef219dcd8da262cbb958639a96bedba10e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/d543b8ef219dcd8da262cbb958639a96bedba10e", + "reference": "d543b8ef219dcd8da262cbb958639a96bedba10e", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.7.0", + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.5.25" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/lines-of-code", + "type": "tidelift" + } + ], + "time": "2026-05-19T16:22:07+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "7.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1effe8e9b8e068e9ae228e542d5d11b5d16db894", + "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894", + "shasum": "" + }, + "require": { + "php": ">=8.3", + "sebastian/object-reflector": "^5.0", + "sebastian/recursion-context": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/7.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:57:48+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "4bfa827c969c98be1e527abd576533293c634f6a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/4bfa827c969c98be1e527abd576533293c634f6a", + "reference": "4bfa827c969c98be1e527abd576533293c634f6a", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:58:17+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "7.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "0b01998a7d5b1f122911a66bebcb8d46f0c82d8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/0b01998a7d5b1f122911a66bebcb8d46f0c82d8c", + "reference": "0b01998a7d5b1f122911a66bebcb8d46f0c82d8c", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/7.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" + } + ], + "time": "2025-08-13T04:44:59+00:00" + }, + { + "name": "sebastian/type", + "version": "6.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "82ff822c2edc46724be9f7411d3163021f602773" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/82ff822c2edc46724be9f7411d3163021f602773", + "reference": "82ff822c2edc46724be9f7411d3163021f602773", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.5.25" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "security": "https://github.com/sebastianbergmann/type/security/policy", + "source": "https://github.com/sebastianbergmann/type/tree/6.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/type", + "type": "tidelift" + } + ], + "time": "2026-05-20T06:45:45+00:00" + }, + { + "name": "sebastian/version", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/3e6ccf7657d4f0a59200564b08cead899313b53c", + "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "security": "https://github.com/sebastianbergmann/version/security/policy", + "source": "https://github.com/sebastianbergmann/version/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T05:00:38+00:00" + }, + { + "name": "staabm/side-effects-detector", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/staabm/side-effects-detector.git", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.6", + "phpunit/phpunit": "^9.6.21", + "symfony/var-dumper": "^5.4.43", + "tomasvotruba/type-coverage": "1.0.0", + "tomasvotruba/unused-public": "1.0.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "lib/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A static analysis tool to detect side effects in PHP code", + "keywords": [ + "static analysis" + ], + "support": { + "issues": "https://github.com/staabm/side-effects-detector/issues", + "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5" + }, + "funding": [ + { + "url": "https://github.com/staabm", + "type": "github" + } + ], + "time": "2024-10-20T05:08:20+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "7989e43bf381af0eac72e4f0ca5bcbfa81658be4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/7989e43bf381af0eac72e4f0ca5bcbfa81658be4", + "reference": "7989e43bf381af0eac72e4f0ca5bcbfa81658be4", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^8.1" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/2.0.1" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2025-12-08T11:19:18+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": { + "fluttersdk/magic-starter-laravel": 20 + }, + "prefer-stable": true, + "prefer-lowest": false, + "platform": { + "php": "^8.3" + }, + "platform-dev": {}, + "plugin-api-version": "2.9.0" +} diff --git a/backend/config/app.php b/backend/config/app.php new file mode 100644 index 0000000..423eed5 --- /dev/null +++ b/backend/config/app.php @@ -0,0 +1,126 @@ + env('APP_NAME', 'Laravel'), + + /* + |-------------------------------------------------------------------------- + | Application Environment + |-------------------------------------------------------------------------- + | + | This value determines the "environment" your application is currently + | running in. This may determine how you prefer to configure various + | services the application utilizes. Set this in your ".env" file. + | + */ + + 'env' => env('APP_ENV', 'production'), + + /* + |-------------------------------------------------------------------------- + | Application Debug Mode + |-------------------------------------------------------------------------- + | + | When your application is in debug mode, detailed error messages with + | stack traces will be shown on every error that occurs within your + | application. If disabled, a simple generic error page is shown. + | + */ + + 'debug' => (bool) env('APP_DEBUG', false), + + /* + |-------------------------------------------------------------------------- + | Application URL + |-------------------------------------------------------------------------- + | + | This URL is used by the console to properly generate URLs when using + | the Artisan command line tool. You should set this to the root of + | the application so that it's available within Artisan commands. + | + */ + + 'url' => env('APP_URL', 'http://localhost'), + + /* + |-------------------------------------------------------------------------- + | Application Timezone + |-------------------------------------------------------------------------- + | + | Here you may specify the default timezone for your application, which + | will be used by the PHP date and date-time functions. The timezone + | is set to "UTC" by default as it is suitable for most use cases. + | + */ + + 'timezone' => 'UTC', + + /* + |-------------------------------------------------------------------------- + | Application Locale Configuration + |-------------------------------------------------------------------------- + | + | The application locale determines the default locale that will be used + | by Laravel's translation / localization methods. This option can be + | set to any locale for which you plan to have translation strings. + | + */ + + 'locale' => env('APP_LOCALE', 'en'), + + 'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'), + + 'faker_locale' => env('APP_FAKER_LOCALE', 'en_US'), + + /* + |-------------------------------------------------------------------------- + | Encryption Key + |-------------------------------------------------------------------------- + | + | This key is utilized by Laravel's encryption services and should be set + | to a random, 32 character string to ensure that all encrypted values + | are secure. You should do this prior to deploying the application. + | + */ + + 'cipher' => 'AES-256-CBC', + + 'key' => env('APP_KEY'), + + 'previous_keys' => [ + ...array_filter( + explode(',', (string) env('APP_PREVIOUS_KEYS', '')) + ), + ], + + /* + |-------------------------------------------------------------------------- + | Maintenance Mode Driver + |-------------------------------------------------------------------------- + | + | These configuration options determine the driver used to determine and + | manage Laravel's "maintenance mode" status. The "cache" driver will + | allow maintenance mode to be controlled across multiple machines. + | + | Supported drivers: "file", "cache" + | + */ + + 'maintenance' => [ + 'driver' => env('APP_MAINTENANCE_DRIVER', 'file'), + 'store' => env('APP_MAINTENANCE_STORE', 'database'), + ], + +]; diff --git a/backend/config/auth.php b/backend/config/auth.php new file mode 100644 index 0000000..d7568ff --- /dev/null +++ b/backend/config/auth.php @@ -0,0 +1,117 @@ + [ + 'guard' => env('AUTH_GUARD', 'web'), + 'passwords' => env('AUTH_PASSWORD_BROKER', 'users'), + ], + + /* + |-------------------------------------------------------------------------- + | Authentication Guards + |-------------------------------------------------------------------------- + | + | Next, you may define every authentication guard for your application. + | Of course, a great default configuration has been defined for you + | which utilizes session storage plus the Eloquent user provider. + | + | All authentication guards have a user provider, which defines how the + | users are actually retrieved out of your database or other storage + | system used by the application. Typically, Eloquent is utilized. + | + | Supported: "session" + | + */ + + 'guards' => [ + 'web' => [ + 'driver' => 'session', + 'provider' => 'users', + ], + ], + + /* + |-------------------------------------------------------------------------- + | User Providers + |-------------------------------------------------------------------------- + | + | All authentication guards have a user provider, which defines how the + | users are actually retrieved out of your database or other storage + | system used by the application. Typically, Eloquent is utilized. + | + | If you have multiple user tables or models you may configure multiple + | providers to represent the model / table. These providers may then + | be assigned to any extra authentication guards you have defined. + | + | Supported: "database", "eloquent" + | + */ + + 'providers' => [ + 'users' => [ + 'driver' => 'eloquent', + 'model' => env('AUTH_MODEL', User::class), + ], + + // 'users' => [ + // 'driver' => 'database', + // 'table' => 'users', + // ], + ], + + /* + |-------------------------------------------------------------------------- + | Resetting Passwords + |-------------------------------------------------------------------------- + | + | These configuration options specify the behavior of Laravel's password + | reset functionality, including the table utilized for token storage + | and the user provider that is invoked to actually retrieve users. + | + | The expiry time is the number of minutes that each reset token will be + | considered valid. This security feature keeps tokens short-lived so + | they have less time to be guessed. You may change this as needed. + | + | The throttle setting is the number of seconds a user must wait before + | generating more password reset tokens. This prevents the user from + | quickly generating a very large amount of password reset tokens. + | + */ + + 'passwords' => [ + 'users' => [ + 'provider' => 'users', + 'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'), + 'expire' => 60, + 'throttle' => 60, + ], + ], + + /* + |-------------------------------------------------------------------------- + | Password Confirmation Timeout + |-------------------------------------------------------------------------- + | + | Here you may define the number of seconds before a password confirmation + | window expires and users are asked to re-enter their password via the + | confirmation screen. By default, the timeout lasts for three hours. + | + */ + + 'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800), + +]; diff --git a/backend/config/cache.php b/backend/config/cache.php new file mode 100644 index 0000000..d7eec61 --- /dev/null +++ b/backend/config/cache.php @@ -0,0 +1,136 @@ + env('CACHE_STORE', 'database'), + + /* + |-------------------------------------------------------------------------- + | Cache Stores + |-------------------------------------------------------------------------- + | + | Here you may define all of the cache "stores" for your application as + | well as their drivers. You may even define multiple stores for the + | same cache driver to group types of items stored in your caches. + | + | Supported drivers: "array", "database", "file", "memcached", + | "redis", "dynamodb", "storage", "octane", + | "session", "failover", "null" + | + */ + + 'stores' => [ + + 'array' => [ + 'driver' => 'array', + 'serialize' => false, + ], + + 'database' => [ + 'driver' => 'database', + 'connection' => env('DB_CACHE_CONNECTION'), + 'table' => env('DB_CACHE_TABLE', 'cache'), + 'lock_connection' => env('DB_CACHE_LOCK_CONNECTION'), + 'lock_table' => env('DB_CACHE_LOCK_TABLE'), + ], + + 'file' => [ + 'driver' => 'file', + 'path' => storage_path('framework/cache/data'), + 'lock_path' => storage_path('framework/cache/data'), + ], + + 'storage' => [ + 'driver' => 'storage', + 'disk' => env('CACHE_STORAGE_DISK'), + 'path' => env('CACHE_STORAGE_PATH', 'framework/cache/data'), + ], + + 'memcached' => [ + 'driver' => 'memcached', + 'persistent_id' => env('MEMCACHED_PERSISTENT_ID'), + 'sasl' => [ + env('MEMCACHED_USERNAME'), + env('MEMCACHED_PASSWORD'), + ], + 'options' => [ + // Memcached::OPT_CONNECT_TIMEOUT => 2000, + ], + 'servers' => [ + [ + 'host' => env('MEMCACHED_HOST', '127.0.0.1'), + 'port' => env('MEMCACHED_PORT', 11211), + 'weight' => 100, + ], + ], + ], + + 'redis' => [ + 'driver' => 'redis', + 'connection' => env('REDIS_CACHE_CONNECTION', 'cache'), + 'lock_connection' => env('REDIS_CACHE_LOCK_CONNECTION', 'default'), + ], + + 'dynamodb' => [ + 'driver' => 'dynamodb', + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), + 'table' => env('DYNAMODB_CACHE_TABLE', 'cache'), + 'endpoint' => env('DYNAMODB_ENDPOINT'), + ], + + 'octane' => [ + 'driver' => 'octane', + ], + + 'failover' => [ + 'driver' => 'failover', + 'stores' => [ + 'database', + 'array', + ], + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Cache Key Prefix + |-------------------------------------------------------------------------- + | + | When utilizing the APC, database, memcached, Redis, and DynamoDB cache + | stores, there might be other applications using the same cache. For + | that reason, you may prefix every cache key to avoid collisions. + | + */ + + 'prefix' => env('CACHE_PREFIX', Str::slug((string) env('APP_NAME', 'laravel')).'-cache-'), + + /* + |-------------------------------------------------------------------------- + | Serializable Classes + |-------------------------------------------------------------------------- + | + | This value determines the classes that can be unserialized from cache + | storage. By default, no PHP classes will be unserialized from your + | cache to prevent gadget chain attacks if your APP_KEY is leaked. + | + */ + + 'serializable_classes' => false, + +]; diff --git a/backend/config/database.php b/backend/config/database.php new file mode 100644 index 0000000..abbb88e --- /dev/null +++ b/backend/config/database.php @@ -0,0 +1,184 @@ + env('DB_CONNECTION', 'sqlite'), + + /* + |-------------------------------------------------------------------------- + | Database Connections + |-------------------------------------------------------------------------- + | + | Below are all of the database connections defined for your application. + | An example configuration is provided for each database system which + | is supported by Laravel. You're free to add / remove connections. + | + */ + + 'connections' => [ + + 'sqlite' => [ + 'driver' => 'sqlite', + 'url' => env('DB_URL'), + 'database' => env('DB_DATABASE', database_path('database.sqlite')), + 'prefix' => '', + 'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true), + 'busy_timeout' => null, + 'journal_mode' => null, + 'synchronous' => null, + 'transaction_mode' => 'DEFERRED', + ], + + 'mysql' => [ + 'driver' => 'mysql', + 'url' => env('DB_URL'), + 'host' => env('DB_HOST', '127.0.0.1'), + 'port' => env('DB_PORT', '3306'), + 'database' => env('DB_DATABASE', 'laravel'), + 'username' => env('DB_USERNAME', 'root'), + 'password' => env('DB_PASSWORD', ''), + 'unix_socket' => env('DB_SOCKET', ''), + 'charset' => env('DB_CHARSET', 'utf8mb4'), + 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'), + 'prefix' => '', + 'prefix_indexes' => true, + 'strict' => true, + 'engine' => null, + 'options' => extension_loaded('pdo_mysql') ? array_filter([ + Mysql::ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), + ]) : [], + ], + + 'mariadb' => [ + 'driver' => 'mariadb', + 'url' => env('DB_URL'), + 'host' => env('DB_HOST', '127.0.0.1'), + 'port' => env('DB_PORT', '3306'), + 'database' => env('DB_DATABASE', 'laravel'), + 'username' => env('DB_USERNAME', 'root'), + 'password' => env('DB_PASSWORD', ''), + 'unix_socket' => env('DB_SOCKET', ''), + 'charset' => env('DB_CHARSET', 'utf8mb4'), + 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'), + 'prefix' => '', + 'prefix_indexes' => true, + 'strict' => true, + 'engine' => null, + 'options' => extension_loaded('pdo_mysql') ? array_filter([ + Mysql::ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), + ]) : [], + ], + + 'pgsql' => [ + 'driver' => 'pgsql', + 'url' => env('DB_URL'), + 'host' => env('DB_HOST', '127.0.0.1'), + 'port' => env('DB_PORT', '5432'), + 'database' => env('DB_DATABASE', 'laravel'), + 'username' => env('DB_USERNAME', 'root'), + 'password' => env('DB_PASSWORD', ''), + 'charset' => env('DB_CHARSET', 'utf8'), + 'prefix' => '', + 'prefix_indexes' => true, + 'search_path' => 'public', + 'sslmode' => env('DB_SSLMODE', 'prefer'), + ], + + 'sqlsrv' => [ + 'driver' => 'sqlsrv', + 'url' => env('DB_URL'), + 'host' => env('DB_HOST', 'localhost'), + 'port' => env('DB_PORT', '1433'), + 'database' => env('DB_DATABASE', 'laravel'), + 'username' => env('DB_USERNAME', 'root'), + 'password' => env('DB_PASSWORD', ''), + 'charset' => env('DB_CHARSET', 'utf8'), + 'prefix' => '', + 'prefix_indexes' => true, + // 'encrypt' => env('DB_ENCRYPT', 'yes'), + // 'trust_server_certificate' => env('DB_TRUST_SERVER_CERTIFICATE', 'false'), + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Migration Repository Table + |-------------------------------------------------------------------------- + | + | This table keeps track of all the migrations that have already run for + | your application. Using this information, we can determine which of + | the migrations on disk haven't actually been run on the database. + | + */ + + 'migrations' => [ + 'table' => 'migrations', + 'update_date_on_publish' => true, + ], + + /* + |-------------------------------------------------------------------------- + | Redis Databases + |-------------------------------------------------------------------------- + | + | Redis is an open source, fast, and advanced key-value store that also + | provides a richer body of commands than a typical key-value system + | such as Memcached. You may define your connection settings here. + | + */ + + 'redis' => [ + + 'client' => env('REDIS_CLIENT', 'phpredis'), + + 'options' => [ + 'cluster' => env('REDIS_CLUSTER', 'redis'), + 'prefix' => env('REDIS_PREFIX', Str::slug((string) env('APP_NAME', 'laravel')).'-database-'), + 'persistent' => env('REDIS_PERSISTENT', false), + ], + + 'default' => [ + 'url' => env('REDIS_URL'), + 'host' => env('REDIS_HOST', '127.0.0.1'), + 'username' => env('REDIS_USERNAME'), + 'password' => env('REDIS_PASSWORD'), + 'port' => env('REDIS_PORT', '6379'), + 'database' => env('REDIS_DB', '0'), + 'max_retries' => env('REDIS_MAX_RETRIES', 3), + 'backoff_algorithm' => env('REDIS_BACKOFF_ALGORITHM', 'decorrelated_jitter'), + 'backoff_base' => env('REDIS_BACKOFF_BASE', 100), + 'backoff_cap' => env('REDIS_BACKOFF_CAP', 1000), + ], + + 'cache' => [ + 'url' => env('REDIS_URL'), + 'host' => env('REDIS_HOST', '127.0.0.1'), + 'username' => env('REDIS_USERNAME'), + 'password' => env('REDIS_PASSWORD'), + 'port' => env('REDIS_PORT', '6379'), + 'database' => env('REDIS_CACHE_DB', '1'), + 'max_retries' => env('REDIS_MAX_RETRIES', 3), + 'backoff_algorithm' => env('REDIS_BACKOFF_ALGORITHM', 'decorrelated_jitter'), + 'backoff_base' => env('REDIS_BACKOFF_BASE', 100), + 'backoff_cap' => env('REDIS_BACKOFF_CAP', 1000), + ], + + ], + +]; diff --git a/backend/config/filesystems.php b/backend/config/filesystems.php new file mode 100644 index 0000000..37d8fca --- /dev/null +++ b/backend/config/filesystems.php @@ -0,0 +1,80 @@ + env('FILESYSTEM_DISK', 'local'), + + /* + |-------------------------------------------------------------------------- + | Filesystem Disks + |-------------------------------------------------------------------------- + | + | Below you may configure as many filesystem disks as necessary, and you + | may even configure multiple disks for the same driver. Examples for + | most supported storage drivers are configured here for reference. + | + | Supported drivers: "local", "ftp", "sftp", "s3" + | + */ + + 'disks' => [ + + 'local' => [ + 'driver' => 'local', + 'root' => storage_path('app/private'), + 'serve' => true, + 'throw' => false, + 'report' => false, + ], + + 'public' => [ + 'driver' => 'local', + 'root' => storage_path('app/public'), + 'url' => rtrim(env('APP_URL', 'http://localhost'), '/').'/storage', + 'visibility' => 'public', + 'throw' => false, + 'report' => false, + ], + + 's3' => [ + 'driver' => 's3', + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_DEFAULT_REGION'), + 'bucket' => env('AWS_BUCKET'), + 'url' => env('AWS_URL'), + 'endpoint' => env('AWS_ENDPOINT'), + 'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false), + 'throw' => false, + 'report' => false, + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Symbolic Links + |-------------------------------------------------------------------------- + | + | Here you may configure the symbolic links that will be created when the + | `storage:link` Artisan command is executed. The array keys should be + | the locations of the links and the values should be their targets. + | + */ + + 'links' => [ + public_path('storage') => storage_path('app/public'), + ], + +]; diff --git a/backend/config/logging.php b/backend/config/logging.php new file mode 100644 index 0000000..b09cb25 --- /dev/null +++ b/backend/config/logging.php @@ -0,0 +1,132 @@ + env('LOG_CHANNEL', 'stack'), + + /* + |-------------------------------------------------------------------------- + | Deprecations Log Channel + |-------------------------------------------------------------------------- + | + | This option controls the log channel that should be used to log warnings + | regarding deprecated PHP and library features. This allows you to get + | your application ready for upcoming major versions of dependencies. + | + */ + + 'deprecations' => [ + 'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'), + 'trace' => env('LOG_DEPRECATIONS_TRACE', false), + ], + + /* + |-------------------------------------------------------------------------- + | Log Channels + |-------------------------------------------------------------------------- + | + | Here you may configure the log channels for your application. Laravel + | utilizes the Monolog PHP logging library, which includes a variety + | of powerful log handlers and formatters that you're free to use. + | + | Available drivers: "single", "daily", "slack", "syslog", + | "errorlog", "monolog", "custom", "stack" + | + */ + + 'channels' => [ + + 'stack' => [ + 'driver' => 'stack', + 'channels' => explode(',', (string) env('LOG_STACK', 'single')), + 'ignore_exceptions' => false, + ], + + 'single' => [ + 'driver' => 'single', + 'path' => storage_path('logs/laravel.log'), + 'level' => env('LOG_LEVEL', 'debug'), + 'replace_placeholders' => true, + ], + + 'daily' => [ + 'driver' => 'daily', + 'path' => storage_path('logs/laravel.log'), + 'level' => env('LOG_LEVEL', 'debug'), + 'days' => env('LOG_DAILY_DAYS', 14), + 'replace_placeholders' => true, + ], + + 'slack' => [ + 'driver' => 'slack', + 'url' => env('LOG_SLACK_WEBHOOK_URL'), + 'username' => env('LOG_SLACK_USERNAME', env('APP_NAME', 'Laravel')), + 'emoji' => env('LOG_SLACK_EMOJI', ':boom:'), + 'level' => env('LOG_LEVEL', 'critical'), + 'replace_placeholders' => true, + ], + + 'papertrail' => [ + 'driver' => 'monolog', + 'level' => env('LOG_LEVEL', 'debug'), + 'handler' => env('LOG_PAPERTRAIL_HANDLER', SyslogUdpHandler::class), + 'handler_with' => [ + 'host' => env('PAPERTRAIL_URL'), + 'port' => env('PAPERTRAIL_PORT'), + 'connectionString' => 'tls://'.env('PAPERTRAIL_URL').':'.env('PAPERTRAIL_PORT'), + ], + 'processors' => [PsrLogMessageProcessor::class], + ], + + 'stderr' => [ + 'driver' => 'monolog', + 'level' => env('LOG_LEVEL', 'debug'), + 'handler' => StreamHandler::class, + 'handler_with' => [ + 'stream' => 'php://stderr', + ], + 'formatter' => env('LOG_STDERR_FORMATTER'), + 'processors' => [PsrLogMessageProcessor::class], + ], + + 'syslog' => [ + 'driver' => 'syslog', + 'level' => env('LOG_LEVEL', 'debug'), + 'facility' => env('LOG_SYSLOG_FACILITY', LOG_USER), + 'replace_placeholders' => true, + ], + + 'errorlog' => [ + 'driver' => 'errorlog', + 'level' => env('LOG_LEVEL', 'debug'), + 'replace_placeholders' => true, + ], + + 'null' => [ + 'driver' => 'monolog', + 'handler' => NullHandler::class, + ], + + 'emergency' => [ + 'path' => storage_path('logs/laravel.log'), + ], + + ], + +]; diff --git a/backend/config/magic-starter.php b/backend/config/magic-starter.php new file mode 100644 index 0000000..c927a97 --- /dev/null +++ b/backend/config/magic-starter.php @@ -0,0 +1,298 @@ + false, + + /* + |-------------------------------------------------------------------------- + | Feature Flags + |-------------------------------------------------------------------------- + | + | Enable or disable package features. Each feature is a string constant + | defined on the Features class. Disabled features are simply omitted + | from this array. + | + */ + + 'features' => [ + \FlutterSdk\MagicStarter\Features::twoFactorAuthentication(), + \FlutterSdk\MagicStarter\Features::teams(), + // \FlutterSdk\MagicStarter\Features::profilePhotos(), + \FlutterSdk\MagicStarter\Features::sessions(), + // \FlutterSdk\MagicStarter\Features::socialLogin(), + // \FlutterSdk\MagicStarter\Features::newsletterSubscription(), + // \FlutterSdk\MagicStarter\Features::extendedProfile(), + // \FlutterSdk\MagicStarter\Features::notifications(), + // \FlutterSdk\MagicStarter\Features::onesignal(), + // \FlutterSdk\MagicStarter\Features::guestAuth(), + // \FlutterSdk\MagicStarter\Features::phoneOtp(), + \FlutterSdk\MagicStarter\Features::emailVerification(), + // \FlutterSdk\MagicStarter\Features::timezones(), + ], + + /* + |-------------------------------------------------------------------------- + | Frontend URL + |-------------------------------------------------------------------------- + | + | The URL of the frontend application that will consume the API provided by + | this package. This is used when sending email invitations to teams, so + | that the links in the email point to the correct frontend application. + | + */ + + 'frontend_url' => env('MAGIC_STARTER_FRONTEND_URL'), + + /* + |-------------------------------------------------------------------------- + | Models + |-------------------------------------------------------------------------- + | + | Override the default Eloquent models used by the package. + | + */ + + 'models' => [ + 'user' => env('MAGIC_STARTER_USER_MODEL'), + 'team' => env('MAGIC_STARTER_TEAM_MODEL', Team::class), + 'membership' => env('MAGIC_STARTER_MEMBERSHIP_MODEL', TeamUser::class), + 'team_invitation' => env('MAGIC_STARTER_TEAM_INVITATION_MODEL', TeamInvitation::class), + ], + + /* + |-------------------------------------------------------------------------- + | Locale & Timezone + |-------------------------------------------------------------------------- + | + | Default locale and timezone for new users. These values are used when + | creating a new user account and can be updated by the user later. + | + | When the client sends Accept-Language or X-Timezone headers during + | registration, the package auto-detects values from those headers and + | validates them against the supported lists below. + | + */ + + 'defaults' => [ + 'locale' => env('MAGIC_STARTER_DEFAULT_LOCALE', 'en'), + 'timezone' => env('MAGIC_STARTER_DEFAULT_TIMEZONE', 'UTC'), + ], + + /* + |-------------------------------------------------------------------------- + | Supported Locales + |-------------------------------------------------------------------------- + | + | The list of locale codes your application supports. Used for validation + | during registration and profile updates, and for auto-detection from + | the Accept-Language header. Locale codes should be 2-letter ISO 639-1. + | + */ + + 'supported_locales' => [ + 'en', + 'tr', + ], + + /* + |-------------------------------------------------------------------------- + | Profile & Team Photos + |-------------------------------------------------------------------------- + | + | Configure the storage disk, paths, and fallback Avatar generator URL + | for user profile photos and team profile photos. + | + */ + + 'profile_photo_disk' => env('MAGIC_STARTER_PROFILE_PHOTO_DISK', 'public'), + 'team_photo_disk' => env('MAGIC_STARTER_TEAM_PHOTO_DISK', env('MAGIC_STARTER_PROFILE_PHOTO_DISK', 'public')), + 'profile_photo_path' => env('MAGIC_STARTER_PROFILE_PHOTO_PATH', 'profile-photos'), + 'team_photo_path' => env('MAGIC_STARTER_TEAM_PHOTO_PATH', 'team-photos'), + 'ui_avatars_url' => env('MAGIC_STARTER_UI_AVATARS_URL', 'https://ui-avatars.com/api/'), + + /* + |-------------------------------------------------------------------------- + | Route Prefix + |-------------------------------------------------------------------------- + | + | Prefix for all routes registered by this package. + | + */ + + 'route_prefix' => env('MAGIC_STARTER_ROUTE_PREFIX', 'api/v1'), + + /* + |-------------------------------------------------------------------------- + | Team Invitation Expiry + |-------------------------------------------------------------------------- + | + | Determines the number of days until a team invitation expires. + | + */ + + 'invitation_expiry_days' => env('MAGIC_STARTER_INVITATION_EXPIRY_DAYS', 7), + + /* + |-------------------------------------------------------------------------- + | Token Expiration + |-------------------------------------------------------------------------- + | + | Set the number of minutes until issued tokens expire. Null means + | tokens never expire. Configure Sanctum's pruning command to clean + | up expired tokens: php artisan sanctum:prune-expired --hours=24 + | + */ + + 'token_expiration_minutes' => env('MAGIC_STARTER_TOKEN_EXPIRATION', null), + + /* + |-------------------------------------------------------------------------- + | Authentication Identity + |-------------------------------------------------------------------------- + | + | Configure which identity fields are accepted during registration and + | login. Both can be enabled simultaneously — in that case, at least one + | identifier is required. + | + | - email: true → users may register/login with an email address + | - phone: true → users may register/login with a phone number + | + | When both are true, the register and login forms accept either or both. + | When only one is true, that identifier becomes required. + | + */ + + 'auth' => [ + 'email' => (bool) env('MAGIC_STARTER_AUTH_EMAIL', true), + 'phone' => (bool) env('MAGIC_STARTER_AUTH_PHONE', false), + ], + + /* + |-------------------------------------------------------------------------- + | Two-Factor Authentication + |-------------------------------------------------------------------------- + | + | Configure the settings for Two-Factor Authentication (2FA). This includes + | the company name displayed in authenticator apps, the number of recovery + | codes to generate, and the TTL for the challenge token. + | + */ + + 'two_factor' => [ + /* + |-------------------------------------------------------------------------- + | Company Name + |-------------------------------------------------------------------------- + | + | The name of your company or application as it will appear in the + | user's authenticator app (e.g., Google Authenticator, Authy). + | + */ + + 'company_name' => env('APP_NAME', 'Laravel'), + + /* + |-------------------------------------------------------------------------- + | Recovery Codes Count + |-------------------------------------------------------------------------- + | + | The number of multi-use recovery codes that should be generated for + | the user when they enable two-factor authentication. + | + */ + + 'recovery_codes_count' => 8, + + /* + |-------------------------------------------------------------------------- + | GeoIP Database Path + |-------------------------------------------------------------------------- + | + | The absolute path to the MaxMind GeoIP2 database file (.mmdb) used to + | resolve location data for 2FA challenge attempts. Set to null to + | disable location resolution. + | + */ + + 'geoip_db_path' => null, + + /* + |-------------------------------------------------------------------------- + | Challenge Token TTL + |-------------------------------------------------------------------------- + | + | The number of minutes a two-factor authentication challenge token is + | valid for. Users must complete the challenge within this window. + | + */ + + 'challenge_token_ttl' => 5, + ], + + /* + |-------------------------------------------------------------------------- + | OneSignal Push Notifications + |-------------------------------------------------------------------------- + | + | Configure the settings for OneSignal push notifications. This includes + | the app ID, REST API key, and the default target channel for push messages. + | + */ + + 'onesignal' => [ + /* + |-------------------------------------------------------------------------- + | OneSignal App ID + |-------------------------------------------------------------------------- + | + | The OneSignal application ID for your project. Required to send push + | notifications via the OneSignal PHP SDK. + | + */ + + 'app_id' => env('ONESIGNAL_APP_ID'), + + /* + |-------------------------------------------------------------------------- + | OneSignal REST API Key + |-------------------------------------------------------------------------- + | + | The OneSignal REST API key for server-to-server authentication. + | Required to send push notifications via the OneSignal PHP SDK. + | + */ + + 'rest_api_key' => env('ONESIGNAL_REST_API_KEY'), + + /* + |-------------------------------------------------------------------------- + | OneSignal Target Channel + |-------------------------------------------------------------------------- + | + | The default delivery channel for OneSignal push notifications. + | Typically 'push' for native mobile push notifications. + | + */ + + 'target_channel' => 'push', + ], +]; diff --git a/backend/config/mail.php b/backend/config/mail.php new file mode 100644 index 0000000..e32e88d --- /dev/null +++ b/backend/config/mail.php @@ -0,0 +1,118 @@ + env('MAIL_MAILER', 'log'), + + /* + |-------------------------------------------------------------------------- + | Mailer Configurations + |-------------------------------------------------------------------------- + | + | Here you may configure all of the mailers used by your application plus + | their respective settings. Several examples have been configured for + | you and you are free to add your own as your application requires. + | + | Laravel supports a variety of mail "transport" drivers that can be used + | when delivering an email. You may specify which one you're using for + | your mailers below. You may also add additional mailers if needed. + | + | Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2", + | "postmark", "resend", "log", "array", + | "failover", "roundrobin" + | + */ + + 'mailers' => [ + + 'smtp' => [ + 'transport' => 'smtp', + 'scheme' => env('MAIL_SCHEME'), + 'url' => env('MAIL_URL'), + 'host' => env('MAIL_HOST', '127.0.0.1'), + 'port' => env('MAIL_PORT', 2525), + 'username' => env('MAIL_USERNAME'), + 'password' => env('MAIL_PASSWORD'), + 'timeout' => null, + 'local_domain' => env('MAIL_EHLO_DOMAIN', parse_url((string) env('APP_URL', 'http://localhost'), PHP_URL_HOST)), + ], + + 'ses' => [ + 'transport' => 'ses', + ], + + 'postmark' => [ + 'transport' => 'postmark', + // 'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'), + // 'client' => [ + // 'timeout' => 5, + // ], + ], + + 'resend' => [ + 'transport' => 'resend', + ], + + 'sendmail' => [ + 'transport' => 'sendmail', + 'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -bs -i'), + ], + + 'log' => [ + 'transport' => 'log', + 'channel' => env('MAIL_LOG_CHANNEL'), + ], + + 'array' => [ + 'transport' => 'array', + ], + + 'failover' => [ + 'transport' => 'failover', + 'mailers' => [ + 'smtp', + 'log', + ], + 'retry_after' => 60, + ], + + 'roundrobin' => [ + 'transport' => 'roundrobin', + 'mailers' => [ + 'ses', + 'postmark', + ], + 'retry_after' => 60, + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Global "From" Address + |-------------------------------------------------------------------------- + | + | You may wish for all emails sent by your application to be sent from + | the same address. Here you may specify a name and address that is + | used globally for all emails that are sent by your application. + | + */ + + 'from' => [ + 'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'), + 'name' => env('MAIL_FROM_NAME', env('APP_NAME', 'Laravel')), + ], + +]; diff --git a/backend/config/queue.php b/backend/config/queue.php new file mode 100644 index 0000000..79c2c0a --- /dev/null +++ b/backend/config/queue.php @@ -0,0 +1,129 @@ + env('QUEUE_CONNECTION', 'database'), + + /* + |-------------------------------------------------------------------------- + | Queue Connections + |-------------------------------------------------------------------------- + | + | Here you may configure the connection options for every queue backend + | used by your application. An example configuration is provided for + | each backend supported by Laravel. You're also free to add more. + | + | Drivers: "sync", "database", "beanstalkd", "sqs", "redis", + | "deferred", "background", "failover", "null" + | + */ + + 'connections' => [ + + 'sync' => [ + 'driver' => 'sync', + ], + + 'database' => [ + 'driver' => 'database', + 'connection' => env('DB_QUEUE_CONNECTION'), + 'table' => env('DB_QUEUE_TABLE', 'jobs'), + 'queue' => env('DB_QUEUE', 'default'), + 'retry_after' => (int) env('DB_QUEUE_RETRY_AFTER', 90), + 'after_commit' => false, + ], + + 'beanstalkd' => [ + 'driver' => 'beanstalkd', + 'host' => env('BEANSTALKD_QUEUE_HOST', 'localhost'), + 'queue' => env('BEANSTALKD_QUEUE', 'default'), + 'retry_after' => (int) env('BEANSTALKD_QUEUE_RETRY_AFTER', 90), + 'block_for' => 0, + 'after_commit' => false, + ], + + 'sqs' => [ + 'driver' => 'sqs', + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'), + 'queue' => env('SQS_QUEUE', 'default'), + 'suffix' => env('SQS_SUFFIX'), + 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), + 'after_commit' => false, + ], + + 'redis' => [ + 'driver' => 'redis', + 'connection' => env('REDIS_QUEUE_CONNECTION', 'default'), + 'queue' => env('REDIS_QUEUE', 'default'), + 'retry_after' => (int) env('REDIS_QUEUE_RETRY_AFTER', 90), + 'block_for' => null, + 'after_commit' => false, + ], + + 'deferred' => [ + 'driver' => 'deferred', + ], + + 'background' => [ + 'driver' => 'background', + ], + + 'failover' => [ + 'driver' => 'failover', + 'connections' => [ + 'database', + 'deferred', + ], + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Job Batching + |-------------------------------------------------------------------------- + | + | The following options configure the database and table that store job + | batching information. These options can be updated to any database + | connection and table which has been defined by your application. + | + */ + + 'batching' => [ + 'database' => env('DB_CONNECTION', 'sqlite'), + 'table' => 'job_batches', + ], + + /* + |-------------------------------------------------------------------------- + | Failed Queue Jobs + |-------------------------------------------------------------------------- + | + | These options configure the behavior of failed queue job logging so you + | can control how and where failed jobs are stored. Laravel ships with + | support for storing failed jobs in a simple file or in a database. + | + | Supported drivers: "database-uuids", "dynamodb", "file", "null" + | + */ + + 'failed' => [ + 'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'), + 'database' => env('DB_CONNECTION', 'sqlite'), + 'table' => 'failed_jobs', + ], + +]; diff --git a/backend/config/services.php b/backend/config/services.php new file mode 100644 index 0000000..6a90eb8 --- /dev/null +++ b/backend/config/services.php @@ -0,0 +1,38 @@ + [ + 'key' => env('POSTMARK_API_KEY'), + ], + + 'resend' => [ + 'key' => env('RESEND_API_KEY'), + ], + + 'ses' => [ + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), + ], + + 'slack' => [ + 'notifications' => [ + 'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'), + 'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'), + ], + ], + +]; diff --git a/backend/config/session.php b/backend/config/session.php new file mode 100644 index 0000000..f574482 --- /dev/null +++ b/backend/config/session.php @@ -0,0 +1,233 @@ + env('SESSION_DRIVER', 'database'), + + /* + |-------------------------------------------------------------------------- + | Session Lifetime + |-------------------------------------------------------------------------- + | + | Here you may specify the number of minutes that you wish the session + | to be allowed to remain idle before it expires. If you want them + | to expire immediately when the browser is closed then you may + | indicate that via the expire_on_close configuration option. + | + */ + + 'lifetime' => (int) env('SESSION_LIFETIME', 120), + + 'expire_on_close' => env('SESSION_EXPIRE_ON_CLOSE', false), + + /* + |-------------------------------------------------------------------------- + | Session Encryption + |-------------------------------------------------------------------------- + | + | This option allows you to easily specify that all of your session data + | should be encrypted before it's stored. All encryption is performed + | automatically by Laravel and you may use the session like normal. + | + */ + + 'encrypt' => env('SESSION_ENCRYPT', false), + + /* + |-------------------------------------------------------------------------- + | Session File Location + |-------------------------------------------------------------------------- + | + | When utilizing the "file" session driver, the session files are placed + | on disk. The default storage location is defined here; however, you + | are free to provide another location where they should be stored. + | + */ + + 'files' => storage_path('framework/sessions'), + + /* + |-------------------------------------------------------------------------- + | Session Database Connection + |-------------------------------------------------------------------------- + | + | When using the "database" or "redis" session drivers, you may specify a + | connection that should be used to manage these sessions. This should + | correspond to a connection in your database configuration options. + | + */ + + 'connection' => env('SESSION_CONNECTION'), + + /* + |-------------------------------------------------------------------------- + | Session Database Table + |-------------------------------------------------------------------------- + | + | When using the "database" session driver, you may specify the table to + | be used to store sessions. Of course, a sensible default is defined + | for you; however, you're welcome to change this to another table. + | + */ + + 'table' => env('SESSION_TABLE', 'sessions'), + + /* + |-------------------------------------------------------------------------- + | Session Cache Store + |-------------------------------------------------------------------------- + | + | When using one of the framework's cache driven session backends, you may + | define the cache store which should be used to store the session data + | between requests. This must match one of your defined cache stores. + | + | Affects: "dynamodb", "memcached", "redis" + | + */ + + 'store' => env('SESSION_STORE'), + + /* + |-------------------------------------------------------------------------- + | Session Sweeping Lottery + |-------------------------------------------------------------------------- + | + | Some session drivers must manually sweep their storage location to get + | rid of old sessions from storage. Here are the chances that it will + | happen on a given request. By default, the odds are 2 out of 100. + | + */ + + 'lottery' => [2, 100], + + /* + |-------------------------------------------------------------------------- + | Session Cookie Name + |-------------------------------------------------------------------------- + | + | Here you may change the name of the session cookie that is created by + | the framework. Typically, you should not need to change this value + | since doing so does not grant a meaningful security improvement. + | + */ + + 'cookie' => env( + 'SESSION_COOKIE', + Str::slug((string) env('APP_NAME', 'laravel')).'-session' + ), + + /* + |-------------------------------------------------------------------------- + | Session Cookie Path + |-------------------------------------------------------------------------- + | + | The session cookie path determines the path for which the cookie will + | be regarded as available. Typically, this will be the root path of + | your application, but you're free to change this when necessary. + | + */ + + 'path' => env('SESSION_PATH', '/'), + + /* + |-------------------------------------------------------------------------- + | Session Cookie Domain + |-------------------------------------------------------------------------- + | + | This value determines the domain and subdomains the session cookie is + | available to. By default, the cookie will be available to the root + | domain without subdomains. Typically, this shouldn't be changed. + | + */ + + 'domain' => env('SESSION_DOMAIN'), + + /* + |-------------------------------------------------------------------------- + | HTTPS Only Cookies + |-------------------------------------------------------------------------- + | + | By setting this option to true, session cookies will only be sent back + | to the server if the browser has a HTTPS connection. This will keep + | the cookie from being sent to you when it can't be done securely. + | + */ + + 'secure' => env('SESSION_SECURE_COOKIE'), + + /* + |-------------------------------------------------------------------------- + | HTTP Access Only + |-------------------------------------------------------------------------- + | + | Setting this value to true will prevent JavaScript from accessing the + | value of the cookie and the cookie will only be accessible through + | the HTTP protocol. It's unlikely you should disable this option. + | + */ + + 'http_only' => env('SESSION_HTTP_ONLY', true), + + /* + |-------------------------------------------------------------------------- + | Same-Site Cookies + |-------------------------------------------------------------------------- + | + | This option determines how your cookies behave when cross-site requests + | take place, and can be used to mitigate CSRF attacks. By default, we + | will set this value to "lax" to permit secure cross-site requests. + | + | See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value + | + | Supported: "lax", "strict", "none", null + | + */ + + 'same_site' => env('SESSION_SAME_SITE', 'lax'), + + /* + |-------------------------------------------------------------------------- + | Partitioned Cookies + |-------------------------------------------------------------------------- + | + | Setting this value to true will tie the cookie to the top-level site for + | a cross-site context. Partitioned cookies are accepted by the browser + | when flagged "secure" and the Same-Site attribute is set to "none". + | + */ + + 'partitioned' => env('SESSION_PARTITIONED_COOKIE', false), + + /* + |-------------------------------------------------------------------------- + | Session Serialization + |-------------------------------------------------------------------------- + | + | This value controls the serialization strategy for session data, which + | is JSON by default. Setting this to "php" allows the storage of PHP + | objects in the session but can make an application vulnerable to + | "gadget chain" serialization attacks if the APP_KEY is leaked. + | + | Supported: "json", "php" + | + */ + + 'serialization' => 'json', + +]; diff --git a/backend/database/.gitignore b/backend/database/.gitignore new file mode 100644 index 0000000..9b19b93 --- /dev/null +++ b/backend/database/.gitignore @@ -0,0 +1 @@ +*.sqlite* diff --git a/backend/database/factories/UserFactory.php b/backend/database/factories/UserFactory.php new file mode 100644 index 0000000..c4ceb07 --- /dev/null +++ b/backend/database/factories/UserFactory.php @@ -0,0 +1,45 @@ + + */ +class UserFactory extends Factory +{ + /** + * The current password being used by the factory. + */ + protected static ?string $password; + + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'name' => fake()->name(), + 'email' => fake()->unique()->safeEmail(), + 'email_verified_at' => now(), + 'password' => static::$password ??= Hash::make('password'), + 'remember_token' => Str::random(10), + ]; + } + + /** + * Indicate that the model's email address should be unverified. + */ + public function unverified(): static + { + return $this->state(fn (array $attributes) => [ + 'email_verified_at' => null, + ]); + } +} diff --git a/backend/database/migrations/0001_01_01_000000_create_users_table.php b/backend/database/migrations/0001_01_01_000000_create_users_table.php new file mode 100644 index 0000000..05fb5d9 --- /dev/null +++ b/backend/database/migrations/0001_01_01_000000_create_users_table.php @@ -0,0 +1,49 @@ +id(); + $table->string('name'); + $table->string('email')->unique(); + $table->timestamp('email_verified_at')->nullable(); + $table->string('password'); + $table->rememberToken(); + $table->timestamps(); + }); + + Schema::create('password_reset_tokens', function (Blueprint $table) { + $table->string('email')->primary(); + $table->string('token'); + $table->timestamp('created_at')->nullable(); + }); + + Schema::create('sessions', function (Blueprint $table) { + $table->string('id')->primary(); + $table->foreignId('user_id')->nullable()->index(); + $table->string('ip_address', 45)->nullable(); + $table->text('user_agent')->nullable(); + $table->longText('payload'); + $table->integer('last_activity')->index(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('users'); + Schema::dropIfExists('password_reset_tokens'); + Schema::dropIfExists('sessions'); + } +}; diff --git a/backend/database/migrations/0001_01_01_000001_create_cache_table.php b/backend/database/migrations/0001_01_01_000001_create_cache_table.php new file mode 100644 index 0000000..06dc7a5 --- /dev/null +++ b/backend/database/migrations/0001_01_01_000001_create_cache_table.php @@ -0,0 +1,35 @@ +string('key')->primary(); + $table->mediumText('value'); + $table->bigInteger('expiration')->index(); + }); + + Schema::create('cache_locks', function (Blueprint $table) { + $table->string('key')->primary(); + $table->string('owner'); + $table->bigInteger('expiration')->index(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('cache'); + Schema::dropIfExists('cache_locks'); + } +}; diff --git a/backend/database/migrations/0001_01_01_000002_create_jobs_table.php b/backend/database/migrations/0001_01_01_000002_create_jobs_table.php new file mode 100644 index 0000000..edac6fe --- /dev/null +++ b/backend/database/migrations/0001_01_01_000002_create_jobs_table.php @@ -0,0 +1,59 @@ +id(); + $table->string('queue')->index(); + $table->longText('payload'); + $table->unsignedSmallInteger('attempts'); + $table->unsignedInteger('reserved_at')->nullable(); + $table->unsignedInteger('available_at'); + $table->unsignedInteger('created_at'); + }); + + Schema::create('job_batches', function (Blueprint $table) { + $table->string('id')->primary(); + $table->string('name'); + $table->integer('total_jobs'); + $table->integer('pending_jobs'); + $table->integer('failed_jobs'); + $table->longText('failed_job_ids'); + $table->mediumText('options')->nullable(); + $table->integer('cancelled_at')->nullable(); + $table->integer('created_at'); + $table->integer('finished_at')->nullable(); + }); + + Schema::create('failed_jobs', function (Blueprint $table) { + $table->id(); + $table->string('uuid')->unique(); + $table->string('connection'); + $table->string('queue'); + $table->longText('payload'); + $table->longText('exception'); + $table->timestamp('failed_at')->useCurrent(); + + $table->index(['connection', 'queue', 'failed_at']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('jobs'); + Schema::dropIfExists('job_batches'); + Schema::dropIfExists('failed_jobs'); + } +}; diff --git a/backend/database/migrations/2026_06_24_100010_create_teams_table.php b/backend/database/migrations/2026_06_24_100010_create_teams_table.php new file mode 100644 index 0000000..380384e --- /dev/null +++ b/backend/database/migrations/2026_06_24_100010_create_teams_table.php @@ -0,0 +1,28 @@ +constrained()->cascadeOnDelete(); + $table->string('name'); + $table->boolean('personal_team')->default(true); + $table->string('profile_photo_path', 2048)->nullable(); + $table->timestamps(); + }); + } + } + + public function down(): void + { + Schema::dropIfExists('teams'); + } +}; diff --git a/backend/database/migrations/2026_06_24_100020_create_team_user_table.php b/backend/database/migrations/2026_06_24_100020_create_team_user_table.php new file mode 100644 index 0000000..23c6734 --- /dev/null +++ b/backend/database/migrations/2026_06_24_100020_create_team_user_table.php @@ -0,0 +1,29 @@ +constrained()->cascadeOnDelete(); + MigrationHelper::foreignKey($table, 'user_id')->constrained()->cascadeOnDelete(); + $table->string('role')->nullable()->default('member'); + $table->timestamps(); + + $table->unique(['team_id', 'user_id']); + }); + } + } + + public function down(): void + { + Schema::dropIfExists('team_user'); + } +}; diff --git a/backend/database/migrations/2026_06_24_100030_create_team_invitations_table.php b/backend/database/migrations/2026_06_24_100030_create_team_invitations_table.php new file mode 100644 index 0000000..5265ffe --- /dev/null +++ b/backend/database/migrations/2026_06_24_100030_create_team_invitations_table.php @@ -0,0 +1,30 @@ +constrained()->cascadeOnDelete(); + $table->string('email'); + $table->string('role')->nullable()->default('member'); + $table->string('token')->unique(); + $table->timestamps(); + + $table->unique(['team_id', 'email']); + }); + } + } + + public function down(): void + { + Schema::dropIfExists('team_invitations'); + } +}; diff --git a/backend/database/migrations/2026_06_24_100040_create_personal_access_tokens_table.php b/backend/database/migrations/2026_06_24_100040_create_personal_access_tokens_table.php new file mode 100644 index 0000000..585ea13 --- /dev/null +++ b/backend/database/migrations/2026_06_24_100040_create_personal_access_tokens_table.php @@ -0,0 +1,32 @@ +text('name'); + $table->string('token', 64)->unique(); + $table->text('abilities')->nullable(); + $table->timestamp('last_used_at')->nullable(); + $table->timestamp('expires_at')->nullable()->index(); + $table->string('ip_address', 45)->nullable(); + $table->text('user_agent')->nullable(); + $table->timestamps(); + }); + } + } + + public function down(): void + { + Schema::dropIfExists('personal_access_tokens'); + } +}; diff --git a/backend/database/migrations/2026_06_24_100050_create_notifications_table.php b/backend/database/migrations/2026_06_24_100050_create_notifications_table.php new file mode 100644 index 0000000..9e0a0f2 --- /dev/null +++ b/backend/database/migrations/2026_06_24_100050_create_notifications_table.php @@ -0,0 +1,40 @@ +string('type'); + MigrationHelper::morphColumns($table, 'notifiable'); + $table->text('data'); + $table->timestamp('read_at')->nullable(); + $table->timestamps(); + + $table->index([ + 'notifiable_type', + 'notifiable_id', + 'read_at', + ]); + }); + } + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('notifications'); + } +}; diff --git a/backend/database/migrations/2026_06_24_100060_create_notification_settings_table.php b/backend/database/migrations/2026_06_24_100060_create_notification_settings_table.php new file mode 100644 index 0000000..314ea4e --- /dev/null +++ b/backend/database/migrations/2026_06_24_100060_create_notification_settings_table.php @@ -0,0 +1,41 @@ +string('type'); + $table->string('channel'); + $table->boolean('is_enabled')->default(true); + $table->timestamps(); + + $table->unique([ + 'notifiable_id', + 'notifiable_type', + 'type', + 'channel', + ], 'notification_settings_unique'); + }); + } + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('notification_settings'); + } +}; diff --git a/backend/database/migrations/2026_06_24_100070_create_newsletter_subscribers_table.php b/backend/database/migrations/2026_06_24_100070_create_newsletter_subscribers_table.php new file mode 100644 index 0000000..65323aa --- /dev/null +++ b/backend/database/migrations/2026_06_24_100070_create_newsletter_subscribers_table.php @@ -0,0 +1,27 @@ +string('email')->unique(); + $table->boolean('is_active')->default(true); + $table->string('source')->nullable(); + $table->timestamps(); + }); + } + } + + public function down(): void + { + Schema::dropIfExists('newsletter_subscribers'); + } +}; diff --git a/backend/database/migrations/2026_06_24_200010_add_current_team_id_to_users_table.php b/backend/database/migrations/2026_06_24_200010_add_current_team_id_to_users_table.php new file mode 100644 index 0000000..30a50cf --- /dev/null +++ b/backend/database/migrations/2026_06_24_200010_add_current_team_id_to_users_table.php @@ -0,0 +1,28 @@ +nullable() + ->constrained('teams') + ->nullOnDelete(); + }); + } + } + + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropConstrainedForeignId('current_team_id'); + }); + } +}; diff --git a/backend/database/migrations/2026_06_24_200020_add_device_info_to_personal_access_tokens_table.php b/backend/database/migrations/2026_06_24_200020_add_device_info_to_personal_access_tokens_table.php new file mode 100644 index 0000000..1d1ccd9 --- /dev/null +++ b/backend/database/migrations/2026_06_24_200020_add_device_info_to_personal_access_tokens_table.php @@ -0,0 +1,35 @@ +string('ip_address', 45)->nullable(); + } + + if (! Schema::hasColumn('personal_access_tokens', 'user_agent')) { + $table->text('user_agent')->nullable(); + } + }); + } + + public function down(): void + { + Schema::table('personal_access_tokens', function (Blueprint $table) { + $columnsToDrop = array_filter([ + Schema::hasColumn('personal_access_tokens', 'ip_address') ? 'ip_address' : null, + Schema::hasColumn('personal_access_tokens', 'user_agent') ? 'user_agent' : null, + ]); + + if (count($columnsToDrop) > 0) { + $table->dropColumn($columnsToDrop); + } + }); + } +}; diff --git a/backend/database/migrations/2026_06_24_200030_add_expires_at_to_team_invitations_table.php b/backend/database/migrations/2026_06_24_200030_add_expires_at_to_team_invitations_table.php new file mode 100644 index 0000000..bcf0265 --- /dev/null +++ b/backend/database/migrations/2026_06_24_200030_add_expires_at_to_team_invitations_table.php @@ -0,0 +1,26 @@ +timestamp('expires_at')->nullable()->after('token'); + }); + } + } + + public function down(): void + { + if (Schema::hasColumn('team_invitations', 'expires_at')) { + Schema::table('team_invitations', function (Blueprint $table): void { + $table->dropColumn('expires_at'); + }); + } + } +}; diff --git a/backend/database/migrations/2026_06_24_200040_add_guest_and_phone_fields_to_users_table.php b/backend/database/migrations/2026_06_24_200040_add_guest_and_phone_fields_to_users_table.php new file mode 100644 index 0000000..f0ce8c6 --- /dev/null +++ b/backend/database/migrations/2026_06_24_200040_add_guest_and_phone_fields_to_users_table.php @@ -0,0 +1,66 @@ +string('email')->nullable()->change(); + $table->string('password')->nullable()->change(); + + if (! Schema::hasColumn('users', 'is_guest')) { + $table->boolean('is_guest') + ->default(false) + ->after('password'); + } + + if (! Schema::hasColumn('users', 'device_id')) { + $table->string('device_id', 255) + ->nullable() + ->unique() + ->after('is_guest'); + } + + if (! Schema::hasColumn('users', 'phone_country')) { + $table->char('phone_country', 2) + ->nullable() + ->after('phone'); + } + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->string('email')->nullable(false)->change(); + $table->string('password')->nullable(false)->change(); + + if (Config::get('database.default') !== 'sqlite') { + if (Schema::hasColumn('users', 'device_id')) { + $table->dropUnique(['device_id']); + } + } + + $columnsToDrop = array_filter([ + Schema::hasColumn('users', 'is_guest') ? 'is_guest' : null, + Schema::hasColumn('users', 'device_id') ? 'device_id' : null, + Schema::hasColumn('users', 'phone_country') ? 'phone_country' : null, + ]); + + if (count($columnsToDrop) > 0) { + $table->dropColumn($columnsToDrop); + } + }); + } +}; diff --git a/backend/database/migrations/2026_06_24_200050_add_localization_fields_to_users_table.php b/backend/database/migrations/2026_06_24_200050_add_localization_fields_to_users_table.php new file mode 100644 index 0000000..554b129 --- /dev/null +++ b/backend/database/migrations/2026_06_24_200050_add_localization_fields_to_users_table.php @@ -0,0 +1,35 @@ +string('locale')->default('en')->after('remember_token'); + } + + if (! Schema::hasColumn('users', 'timezone')) { + $table->string('timezone')->default('UTC')->after('locale'); + } + }); + } + + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $columnsToDrop = array_filter([ + Schema::hasColumn('users', 'locale') ? 'locale' : null, + Schema::hasColumn('users', 'timezone') ? 'timezone' : null, + ]); + + if (count($columnsToDrop) > 0) { + $table->dropColumn($columnsToDrop); + } + }); + } +}; diff --git a/backend/database/migrations/2026_06_24_200060_add_profile_fields_to_users_table.php b/backend/database/migrations/2026_06_24_200060_add_profile_fields_to_users_table.php new file mode 100644 index 0000000..a3397e4 --- /dev/null +++ b/backend/database/migrations/2026_06_24_200060_add_profile_fields_to_users_table.php @@ -0,0 +1,26 @@ +string('phone')->nullable()->after('email'); + } + }); + } + + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + if (Schema::hasColumn('users', 'phone')) { + $table->dropColumn('phone'); + } + }); + } +}; diff --git a/backend/database/migrations/2026_06_24_200070_add_profile_photo_path_to_teams_table.php b/backend/database/migrations/2026_06_24_200070_add_profile_photo_path_to_teams_table.php new file mode 100644 index 0000000..51dec45 --- /dev/null +++ b/backend/database/migrations/2026_06_24_200070_add_profile_photo_path_to_teams_table.php @@ -0,0 +1,26 @@ +string('profile_photo_path', 2048)->nullable()->after('personal_team'); + }); + } + } + + public function down(): void + { + if (Schema::hasColumn('teams', 'profile_photo_path')) { + Schema::table('teams', function (Blueprint $table) { + $table->dropColumn('profile_photo_path'); + }); + } + } +}; diff --git a/backend/database/migrations/2026_06_24_200080_add_profile_photo_path_to_users_table.php b/backend/database/migrations/2026_06_24_200080_add_profile_photo_path_to_users_table.php new file mode 100644 index 0000000..209d6bb --- /dev/null +++ b/backend/database/migrations/2026_06_24_200080_add_profile_photo_path_to_users_table.php @@ -0,0 +1,26 @@ +string('profile_photo_path', 2048)->nullable()->after('email'); + }); + } + } + + public function down(): void + { + if (Schema::hasColumn('users', 'profile_photo_path')) { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('profile_photo_path'); + }); + } + } +}; diff --git a/backend/database/migrations/2026_06_24_200090_add_timezone_to_users_table.php b/backend/database/migrations/2026_06_24_200090_add_timezone_to_users_table.php new file mode 100644 index 0000000..258dc32 --- /dev/null +++ b/backend/database/migrations/2026_06_24_200090_add_timezone_to_users_table.php @@ -0,0 +1,26 @@ +string('timezone')->default('UTC')->after('remember_token'); + } + }); + } + + public function down(): void + { + if (Schema::hasColumn('users', 'timezone')) { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('timezone'); + }); + } + } +}; diff --git a/backend/database/migrations/2026_06_24_200100_add_two_factor_columns_to_users_table.php b/backend/database/migrations/2026_06_24_200100_add_two_factor_columns_to_users_table.php new file mode 100644 index 0000000..38ccd34 --- /dev/null +++ b/backend/database/migrations/2026_06_24_200100_add_two_factor_columns_to_users_table.php @@ -0,0 +1,67 @@ +getTable(); + + Schema::table($tableName, function (Blueprint $table) use ($tableName) { + if (! Schema::hasColumn($tableName, 'two_factor_secret')) { + $table->text('two_factor_secret') + ->after('password') + ->nullable(); + } + + if (! Schema::hasColumn($tableName, 'two_factor_recovery_codes')) { + $table->text('two_factor_recovery_codes') + ->after('two_factor_secret') + ->nullable(); + } + + if (! Schema::hasColumn($tableName, 'two_factor_confirmed_at')) { + $table->timestamp('two_factor_confirmed_at') + ->after('two_factor_recovery_codes') + ->nullable(); + } + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + $userModel = MagicStarter::userModel(); + $tableName = (new $userModel)->getTable(); + + Schema::table($tableName, function (Blueprint $table) use ($tableName) { + $columnsToDrop = []; + + if (Schema::hasColumn($tableName, 'two_factor_secret')) { + $columnsToDrop[] = 'two_factor_secret'; + } + + if (Schema::hasColumn($tableName, 'two_factor_recovery_codes')) { + $columnsToDrop[] = 'two_factor_recovery_codes'; + } + + if (Schema::hasColumn($tableName, 'two_factor_confirmed_at')) { + $columnsToDrop[] = 'two_factor_confirmed_at'; + } + + if (count($columnsToDrop) > 0) { + $table->dropColumn($columnsToDrop); + } + }); + } +}; diff --git a/backend/database/migrations/2026_06_24_200110_drop_language_column_from_users_table.php b/backend/database/migrations/2026_06_24_200110_drop_language_column_from_users_table.php new file mode 100644 index 0000000..ad37539 --- /dev/null +++ b/backend/database/migrations/2026_06_24_200110_drop_language_column_from_users_table.php @@ -0,0 +1,26 @@ +dropColumn('language'); + } + }); + } + + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + if (! Schema::hasColumn('users', 'language')) { + $table->string('language', 10)->nullable()->after('timezone'); + } + }); + } +}; diff --git a/backend/database/seeders/DatabaseSeeder.php b/backend/database/seeders/DatabaseSeeder.php new file mode 100644 index 0000000..6b901f8 --- /dev/null +++ b/backend/database/seeders/DatabaseSeeder.php @@ -0,0 +1,25 @@ +create(); + + User::factory()->create([ + 'name' => 'Test User', + 'email' => 'test@example.com', + ]); + } +} diff --git a/backend/lang/vendor/magic-starter/en/teams.php b/backend/lang/vendor/magic-starter/en/teams.php new file mode 100644 index 0000000..0b01f94 --- /dev/null +++ b/backend/lang/vendor/magic-starter/en/teams.php @@ -0,0 +1,6 @@ + ":name's Team", + 'guest_name' => 'Guest', +]; diff --git a/backend/lang/vendor/magic-starter/tr/teams.php b/backend/lang/vendor/magic-starter/tr/teams.php new file mode 100644 index 0000000..8c98ab7 --- /dev/null +++ b/backend/lang/vendor/magic-starter/tr/teams.php @@ -0,0 +1,6 @@ + ':name Takımı', + 'guest_name' => 'Misafir', +]; diff --git a/backend/package.json b/backend/package.json new file mode 100644 index 0000000..49c869e --- /dev/null +++ b/backend/package.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://www.schemastore.org/package.json", + "private": true, + "type": "module", + "scripts": { + "build": "vite build", + "dev": "vite" + }, + "devDependencies": { + "@tailwindcss/vite": "^4.0.0", + "concurrently": "^9.0.1", + "laravel-vite-plugin": "^3.1", + "tailwindcss": "^4.0.0", + "vite": "^8.0.0" + } +} diff --git a/backend/phpunit.xml b/backend/phpunit.xml new file mode 100644 index 0000000..e7f0a48 --- /dev/null +++ b/backend/phpunit.xml @@ -0,0 +1,36 @@ + + + + + tests/Unit + + + tests/Feature + + + + + app + + + + + + + + + + + + + + + + + + + diff --git a/backend/public/.htaccess b/backend/public/.htaccess new file mode 100644 index 0000000..b574a59 --- /dev/null +++ b/backend/public/.htaccess @@ -0,0 +1,25 @@ + + + Options -MultiViews -Indexes + + + RewriteEngine On + + # Handle Authorization Header + RewriteCond %{HTTP:Authorization} . + RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] + + # Handle X-XSRF-Token Header + RewriteCond %{HTTP:x-xsrf-token} . + RewriteRule .* - [E=HTTP_X_XSRF_TOKEN:%{HTTP:X-XSRF-Token}] + + # Redirect Trailing Slashes If Not A Folder... + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_URI} (.+)/$ + RewriteRule ^ %1 [L,R=301] + + # Send Requests To Front Controller... + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^ index.php [L] + diff --git a/backend/public/favicon.ico b/backend/public/favicon.ico new file mode 100644 index 0000000..e69de29 diff --git a/backend/public/index.php b/backend/public/index.php new file mode 100644 index 0000000..ee8f07e --- /dev/null +++ b/backend/public/index.php @@ -0,0 +1,20 @@ +handleRequest(Request::capture()); diff --git a/backend/public/robots.txt b/backend/public/robots.txt new file mode 100644 index 0000000..eb05362 --- /dev/null +++ b/backend/public/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: diff --git a/backend/resources/css/app.css b/backend/resources/css/app.css new file mode 100644 index 0000000..54b247e --- /dev/null +++ b/backend/resources/css/app.css @@ -0,0 +1,9 @@ +@import 'tailwindcss'; + +@source '../../vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php'; +@source '../../storage/framework/views/*.php'; + +@theme { + --font-sans: 'Instrument Sans', ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', + 'Segoe UI Symbol', 'Noto Color Emoji'; +} diff --git a/backend/resources/js/app.js b/backend/resources/js/app.js new file mode 100644 index 0000000..8337712 --- /dev/null +++ b/backend/resources/js/app.js @@ -0,0 +1 @@ +// diff --git a/backend/resources/views/welcome.blade.php b/backend/resources/views/welcome.blade.php new file mode 100644 index 0000000..26e294a --- /dev/null +++ b/backend/resources/views/welcome.blade.php @@ -0,0 +1,223 @@ + + + + + + + {{ config('app.name', 'Laravel') }} + + @fonts + + + @if (file_exists(public_path('build/manifest.json')) || file_exists(public_path('hot'))) + @vite(['resources/css/app.css', 'resources/js/app.js']) + @else + + @endif + + +
+ @if (Route::has('login')) + + @endif +
+
+
+
+

Let's get started

+

With so many options available to you,
we suggest you start with the following:

+ + + +

+ v{{ app()->version() }} + + View changelog + + + + +

+
+
+ {{-- Laravel Logo --}} + + + + + + + + + + + {{-- 13 --}} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + @if (Route::has('login')) + + @endif + + diff --git a/backend/routes/console.php b/backend/routes/console.php new file mode 100644 index 0000000..3c9adf1 --- /dev/null +++ b/backend/routes/console.php @@ -0,0 +1,8 @@ +comment(Inspiring::quote()); +})->purpose('Display an inspiring quote'); diff --git a/backend/routes/web.php b/backend/routes/web.php new file mode 100644 index 0000000..86a06c5 --- /dev/null +++ b/backend/routes/web.php @@ -0,0 +1,7 @@ +get('/'); + + $response->assertStatus(200); + } +} diff --git a/backend/tests/TestCase.php b/backend/tests/TestCase.php new file mode 100644 index 0000000..fe1ffc2 --- /dev/null +++ b/backend/tests/TestCase.php @@ -0,0 +1,10 @@ +assertTrue(true); + } +} diff --git a/backend/vite.config.js b/backend/vite.config.js new file mode 100644 index 0000000..1fd66d5 --- /dev/null +++ b/backend/vite.config.js @@ -0,0 +1,24 @@ +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; +import { bunny } from 'laravel-vite-plugin/fonts'; +import tailwindcss from '@tailwindcss/vite'; + +export default defineConfig({ + plugins: [ + laravel({ + input: ['resources/css/app.css', 'resources/js/app.js'], + refresh: true, + fonts: [ + bunny('Instrument Sans', { + weights: [400, 500, 600], + }), + ], + }), + tailwindcss(), + ], + server: { + watch: { + ignored: ['**/storage/framework/views/**'], + }, + }, +}); diff --git a/bin/dispatcher.dart b/bin/dispatcher.dart new file mode 100644 index 0000000..67208b1 --- /dev/null +++ b/bin/dispatcher.dart @@ -0,0 +1,38 @@ +import 'dart:io'; + +import 'package:fluttersdk_artisan/artisan.dart'; +import 'package:magic_example/app/commands/_index.g.dart' as consumer_commands; +import 'package:magic_example/app/_plugins.g.dart' as plugins; + +/// GENERATED by the `install` command. DO NOT EDIT. +/// +/// Wires: +/// +/// - consumer commands from `lib/app/commands/` via the codegen barrel +/// `lib/app/commands/_index.g.dart` (regenerated by every +/// `make:command` invocation in the consumer). +/// - plugin providers via `lib/app/_plugins.g.dart` (regenerated by +/// `plugin:install ` / `plugins:refresh` from the +/// `.artisan/plugins.json` registry). +/// +/// New plugins surface automatically after `plugin:install ` AND +/// the parent app's pubspec.yaml lists the plugin under `dependencies:`. +/// No manual edit to this file is required. +class _ConsumerCommandsProvider extends ArtisanServiceProvider { + @override + List commands() => consumer_commands.commands; +} + +Future main(List args) async { + exit( + await runArtisan( + args, + baseProviders: [ + _ConsumerCommandsProvider(), + ...plugins.autoDiscoveredProviders(), + ], + delegateToConsumer: false, + collectMcpTools: args.isNotEmpty && args.first == 'mcp:serve', + ), + ); +} diff --git a/bin/fsa b/bin/fsa new file mode 100755 index 0000000..8a853d0 --- /dev/null +++ b/bin/fsa @@ -0,0 +1,106 @@ +#!/usr/bin/env sh +# bin/fsa: generated by 'dart run fluttersdk_artisan make:fast-cli'. +# Rebuilds the artisan CLI native binary when pubspec.lock or Dart SDK changes. +# POSIX sh; tested on macOS + Linux. +# Re-run 'make:fast-cli --force' to regenerate this wrapper. + +set -e + +# Resolve real path through symlinks without readlink -f (macOS compatible). +follow_links() ( + cd -P -- "$(dirname -- "$1")" + file="$PWD/$(basename -- "$1")" + while [ -h "$file" ]; do + cd -P -- "$(dirname -- "$file")" + file="$(readlink -- "$file")" + cd -P -- "$(dirname -- "$file")" + file="$PWD/$(basename -- "$file")" + done + echo "$file" +) + +# 1. Resolve locations relative to this script. +PROG_NAME="$(follow_links "$0")" +BIN_DIR="$(cd -P -- "$(dirname -- "$PROG_NAME")" && pwd)" +PROJECT_ROOT="$(cd -P -- "$BIN_DIR/.." && pwd)" + +# 2. Define cache paths. +CACHE_DIR="$PROJECT_ROOT/.artisan/cli-bundle" +STAMP_FILE="$PROJECT_ROOT/.artisan/build.stamp" +BINARY="$CACHE_DIR/bundle/bin/dispatcher" +LOCK_DIR="$PROJECT_ROOT/.artisan/.fsa.lock" +ENTRY="$PROJECT_ROOT/bin/dispatcher.dart" + +# 3. Compute a compile key from pubspec.lock hash and Dart SDK version. +LOCK_HASH=$(shasum -a 256 "$PROJECT_ROOT/pubspec.lock" | cut -d ' ' -f 1) +SDK_VER=$(dart --version 2>&1 | awk '{print $4}') +COMPILE_KEY="$LOCK_HASH:$SDK_VER" + +# 4. Determine staleness: binary missing, stamp empty/missing, stamp mismatch, +# or pubspec.yaml has been modified since the last successful build, or +# lib/app/_plugins.g.dart was regenerated by plugin:install or +# plugins:refresh. +# The pubspec.yaml-vs-STAMP_FILE comparison is correct because the stamp is +# written at the end of every successful compile; pubspec.yaml newer than +# the stamp means the user edited it after the last build. Comparing against +# pubspec.lock instead would trip on every freshly installed consumer +# because `dart pub add` updates pubspec.yaml after pub get writes the lock. +needs_build() { + [ ! -x "$BINARY" ] && return 0 + [ ! -s "$STAMP_FILE" ] && return 0 + [ "$(cat "$STAMP_FILE")" != "$COMPILE_KEY" ] && return 0 + [ "$PROJECT_ROOT/pubspec.yaml" -nt "$STAMP_FILE" ] && return 0 + [ "$PROJECT_ROOT/lib/app/_plugins.g.dart" -nt "$STAMP_FILE" ] && return 0 + return 1 +} + +# 5. Acquire the mkdir-based atomic lock. Trap-based release covers +# EXIT / INT / TERM, but SIGKILL (signal 9) bypasses traps and leaves the +# lock dir dangling. The PID-aware staleness check below recovers from +# that case: each holder writes its PID to $LOCK_DIR/pid; a competing +# acquirer probes that PID via `kill -0` and reclaims the lock when the +# owner is dead. POSIX sh; no flock / shlock / bash-isms. +acquire_lock() { + mkdir -p "$(dirname "$LOCK_DIR")" + while ! mkdir "$LOCK_DIR" 2>/dev/null; do + if [ -f "$LOCK_DIR/pid" ]; then + stored_pid="$(cat "$LOCK_DIR/pid" 2>/dev/null)" + if [ -n "$stored_pid" ] && ! kill -0 "$stored_pid" 2>/dev/null; then + # Owner is dead. Reclaim the lock dir and retry mkdir. + rm -rf "$LOCK_DIR" + continue + fi + fi + echo "fsa: waiting for another fsa invocation to finish..." >&2 + sleep 1 + done + echo $$ > "$LOCK_DIR/pid" +} + +if needs_build; then + acquire_lock + + # Release lock on exit, interrupt, or termination. + trap 'rm -rf "$LOCK_DIR" 2>/dev/null' EXIT INT TERM + + # 6. Re-check staleness inside the lock: another invocation may have built + # while we were waiting, making a second compile unnecessary. + if needs_build; then + T=$SECONDS + echo "fsa: building artisan CLI (one-time, ~5s)..." >&2 + + # 7. Compile to AOT binary via dart build cli. + if ! dart build cli -t "$ENTRY" -o "$CACHE_DIR" >&2; then + echo "fsa: dart build cli failed. Run 'dart analyze bin/dispatcher.dart' for details." >&2 + exit 1 + fi + + # 8. Write stamp atomically so a partial compile never leaves a stale stamp. + echo "$COMPILE_KEY" > "$STAMP_FILE.tmp" && mv "$STAMP_FILE.tmp" "$STAMP_FILE" + + echo "fsa: built in $((SECONDS - T))s" >&2 + fi +fi + +# 9. Exec into the compiled binary, replacing this shell process entirely. +exec "$BINARY" "$@" diff --git a/docs/component-registry.md b/docs/component-registry.md new file mode 100644 index 0000000..2026f8e --- /dev/null +++ b/docs/component-registry.md @@ -0,0 +1,510 @@ +--- +generated: manual (design:registry planned) +source: magic_starter generic component library +last_updated: 2026-06-25 +--- + +# Component Registry + +Machine-readable manifest of every component in the `magic_starter` generic component library. Maps each component to its variants, token bindings, and anti-patterns. + +> **design:registry note**: this file is intended to be generated and kept in sync by `make:component` and `previews:refresh`. Until that command emits it automatically, maintain it by hand when adding or modifying components. + +--- + +## Primitives + +Components backed by a Wind W-widget with no recipe layer. + +--- + +## Form Inputs + +### Button + +- **File**: `magic_starter/lib/src/ui/components/button/` +- **Class**: `Button` +- **Recipe**: `WindRecipe` in `button.recipe.dart` +- **Variants**: + - `intent`: `primary` | `secondary` | `ghost` | `destructive` + - `size`: `sm` | `md` | `lg` +- **Default variants**: `intent=primary`, `size=md` +- **Token bindings**: + - `primary`: `bg-primary text-on-primary` + - `secondary`: `bg-surface-container text-fg border border-color-border` + - `ghost`: `bg-transparent text-fg-muted` + - `destructive`: `bg-destructive text-on-destructive` + - `sm`: `text-xs px-3 py-1.5` + - `md`: `text-sm px-4 py-2` + - `lg`: `text-base px-6 py-3` +- **Anti-patterns**: + - Do not use more than one primary button per section. + - Do not use destructive intent outside confirm dialogs without a secondary confirmation step. + - Do not hardcode colors via `className` override when a variant covers the case. + +--- + +### Input + +- **File**: `magic_starter/lib/src/ui/components/input/` +- **Class**: `Input` +- **Recipe**: `WindRecipe` in `input.recipe.dart` +- **Variants**: + - `state`: `default` | `error` +- **Default variants**: `state=default` +- **Token bindings**: + - `default`: `bg-surface-container-high border border-color-border text-fg` + - `error`: `bg-surface-container-high border border-color-destructive text-fg` +- **Anti-patterns**: + - Do not render error state without an error message in the parent `FormField`. + - Do not use raw `WInput` directly; prefer `Input` so the recipe layer is consistent. + +--- + +### Textarea + +- **File**: `magic_starter/lib/src/ui/components/textarea/` +- **Class**: `Textarea` +- **Recipe**: `WindRecipe` in `textarea.recipe.dart` +- **Variants**: + - `state`: `default` | `error` +- **Default variants**: `state=default` +- **Token bindings**: same as Input. +- **Anti-patterns**: same as Input. + +--- + +### Checkbox + +- **File**: `magic_starter/lib/src/ui/components/checkbox/` +- **Class**: `Checkbox` +- **Recipe**: `WindRecipe` in `checkbox.recipe.dart` +- **Variants**: none (state is driven by `checked:` prefix) +- **Token bindings**: + - unchecked: `border-color-border bg-surface-container-high` + - checked (`checked:` state): `bg-primary border-primary` +- **Anti-patterns**: + - Do not use Material `Checkbox`; always use this component. + +--- + +### Switch + +- **File**: `magic_starter/lib/src/ui/components/switch/` +- **Class**: `Switch` +- **Recipe**: `WindRecipe` in `switch.recipe.dart` +- **Variants**: none (state is driven by `checked:` prefix on track/thumb) +- **Token bindings**: + - track off: `bg-surface-container border-color-border` + - track on (`checked:`): `bg-primary` + - thumb: `bg-surface` +- **Anti-patterns**: + - Do not use Material `Switch`. + - Do not animate thumb translate outside the Wind checked state prefix. + +--- + +### Radio + +- **File**: `magic_starter/lib/src/ui/components/radio/` +- **Class**: `Radio` +- **Generic type**: `Radio` +- **Recipe**: `WindRecipe` in `radio.recipe.dart` +- **Variants**: none (state is driven by `selected:` prefix) +- **Token bindings**: + - unselected: `border-color-border bg-surface-container-high` + - selected (`selected:`): `bg-primary border-primary` +- **Anti-patterns**: + - Do not use Material `Radio`. + - Group state management is the caller's responsibility (pass `groupValue`). + +--- + +## Display + +### Badge + +- **File**: `magic_starter/lib/src/ui/components/badge/` +- **Class**: `Badge` +- **Recipe**: `WindRecipe` in `badge.recipe.dart` +- **Variants**: + - `tone`: `neutral` | `primary` | `accent` | `success` | `warning` | `destructive` | `outline` +- **Default variants**: `tone=neutral` +- **Token bindings**: + - `neutral`: `bg-surface-container text-fg-muted` + - `primary`: `bg-primary-container text-primary` + - `accent`: `bg-accent text-on-primary` + - `success`: `bg-success text-on-primary` + - `warning`: `bg-warning text-on-primary` + - `destructive`: `bg-destructive-container text-destructive` + - `outline`: `bg-transparent text-fg border border-color-border` +- **Anti-patterns**: + - Do not use badges for interactive elements; they are display-only. + - Do not use raw hex to create a custom tone; add a new variant value instead. + +--- + +### Typography + +- **File**: `magic_starter/lib/src/ui/components/typography/` +- **Class**: `Typography` +- **Recipe**: `WindRecipe` in `typography.recipe.dart` +- **Variants**: + - `variant`: `h1` | `h2` | `h3` | `body` | `caption` +- **Default variants**: `variant=body` +- **Token bindings**: + - `h1`: `text-3xl font-bold text-fg leading-tight tracking-tight` + - `h2`: `text-2xl font-bold text-fg` + - `h3`: `text-xl font-semibold text-fg` + - `body`: `text-sm text-fg` + - `caption`: `text-xs text-fg-muted` +- **Anti-patterns**: + - Do not use raw `WText` for typographic content; use `Typography` so the scale is consistent. + - Semantics (h1/h2) are secondary to hierarchy; a section title can use `h2` even inside a card. + +--- + +### Skeleton + +- **File**: `magic_starter/lib/src/ui/components/skeleton/` +- **Class**: `Skeleton` +- **Recipe**: `WindRecipe` in `skeleton.recipe.dart` +- **Variants**: + - `shape`: `block` | `text` | `circle` +- **Default variants**: `shape=block` +- **Token bindings**: + - all shapes: `bg-surface-container-high motion-safe:animate-pulse` +- **Anti-patterns**: + - Use `Skeleton` instead of spinners for content loading states. + - Do not animate outside `motion-safe:` prefix (respect `disableAnimations`). + +--- + +## Card + +### Card (migrated from MagicStarterCard) + +- **File**: `magic_starter/lib/src/ui/components/card/` +- **Class**: `Card` +- **Enum**: `CardVariant` +- **Recipe**: `WindRecipe` in `card.recipe.dart` +- **Variants**: + - `tone`: `surface` | `inset` | `elevated` +- **Default variants**: `tone=surface` +- **Token bindings**: + - `surface`: `bg-surface-container border border-color-border` + - `inset`: `bg-surface-container-high` + - `elevated`: `bg-surface shadow-sm` +- **Slots**: `header`, `child` (body), `footer` +- **Anti-patterns**: + - Do not bake CardVariant logic into child components; pass `tone` to `Card` at the call site. + - Do not use `elevated` on dark backgrounds where shadow is invisible; prefer `surface` with a border. + +--- + +## Selection + +### Select + +- **File**: `magic_starter/lib/src/ui/components/select/` +- **Class**: `Select` +- **Recipe**: `WindSlotRecipe` in `select.recipe.dart` +- **Slots**: `trigger`, `popup`, `item` +- **Token bindings**: + - trigger: `bg-surface-container-high border border-color-border text-fg rounded-DEFAULT` + - popup: `bg-surface border border-color-border shadow-sm rounded-md` + - item: `text-sm text-fg hover:bg-surface-container-high` +- **Anti-patterns**: + - Do not use Material `DropdownButton`; use `Select`. + +--- + +### Combobox + +- **File**: `magic_starter/lib/src/ui/components/combobox/` +- **Class**: `Combobox` +- **Recipe**: `WindSlotRecipe` in `combobox.recipe.dart` +- **Slots**: `trigger`, `popup`, `item` +- **Token bindings**: same as Select, plus debounce search input. +- **Anti-patterns**: same as Select. + +--- + +### SegmentedControl + +- **File**: `magic_starter/lib/src/ui/components/segmented_control/` +- **Class**: `SegmentedControl` +- **Recipe**: `WindSlotRecipe` in `segmented_control.recipe.dart` +- **Variants**: + - `size`: `sm` | `md` +- **Slots**: `root`, `item` +- **Token bindings**: + - root: `bg-surface-container rounded-md p-0.5` + - item active (`selected:`): `bg-surface text-fg shadow-sm rounded-sm` + - item inactive: `text-fg-muted` +- **Anti-patterns**: + - Do not use for more than 4-5 options; use `Tabs` or a `Select` instead. + +--- + +### Tabs + +- **File**: `magic_starter/lib/src/ui/components/tabs/` +- **Class**: `Tabs` +- **Recipe**: `WindSlotRecipe` in `tabs.recipe.dart` +- **Slots**: `list`, `tab`, `panel` +- **Token bindings**: + - list: `border-b border-color-border` + - tab inactive: `text-fg-muted` + - tab active (`selected:`): `text-primary border-b-2 border-primary` + - panel: `pt-4` +- **Anti-patterns**: + - Do not use Material `TabBar`; use `Tabs`. + +--- + +### Accordion + +- **File**: `magic_starter/lib/src/ui/components/accordion/` +- **Class**: `Accordion` +- **Recipe**: `WindSlotRecipe` in `accordion.recipe.dart` +- **Slots**: `root`, `item`, `header`, `trigger`, `panel` +- **Token bindings**: + - root: `border border-color-border rounded-md divide-y divide-color-border` + - trigger: `text-fg font-medium` + - panel: `text-fg-muted text-sm px-4 pb-4` +- **Anti-patterns**: + - Do not use for top-level navigation; use for secondary content disclosure only. + +--- + +## Overlays + +### Dialog + +- **File**: `magic_starter/lib/src/ui/components/dialog/` +- **Class**: `Dialog` +- **Recipe**: `WindSlotRecipe` in `dialog.recipe.dart` +- **Slots**: `backdrop`, `panel`, `title`, `footer` +- **Token bindings**: + - backdrop: `bg-fg/50` (semi-transparent fg overlay) + - panel: `bg-surface rounded-lg shadow-xl max-w-md w-full` + - title: `text-fg font-semibold text-lg` + - footer: `flex gap-3 justify-end pt-4` +- **Anti-patterns**: + - Always use `Dialog.show()` static factory; do not push dialogs as routes. + - Keep dialog content focused; avoid multi-step flows inside a single dialog. + +--- + +### ConfirmDialog + +- **File**: `magic_starter/lib/src/ui/components/confirm_dialog/` +- **Class**: `ConfirmDialog` +- **Enum**: `ConfirmDialogVariant` +- **Recipe**: `WindSlotRecipe` in `confirm_dialog.recipe.dart` +- **Variants**: + - `variant`: `primary` | `danger` | `warning` +- **Token bindings**: + - `danger`: confirm button uses `Button(intent: ButtonIntent.destructive)` + - `warning`: confirm button uses `Button(intent: ButtonIntent.secondary)` with warning badge + - `primary`: confirm button uses `Button(intent: ButtonIntent.primary)` +- **Anti-patterns**: + - Use `danger` for irreversible destructive actions only (account deletion, data wipe). + - Do not use `warning` for routine confirmation; reserve it for significant but reversible changes. + +--- + +### BottomSheet + +- **File**: `magic_starter/lib/src/ui/components/bottom_sheet/` +- **Class**: `BottomSheet` +- **Recipe**: `WindSlotRecipe` in `bottom_sheet.recipe.dart` +- **Slots**: `backdrop`, `panel`, `handle`, `title`, `footer` +- **Token bindings**: + - panel: `bg-surface rounded-t-xl` + - handle: `bg-surface-container-high rounded-full` +- **Anti-patterns**: + - Respect `SafeArea` at the bottom for home indicator. + - Do not embed complex multi-step flows; keep to contextual actions. + +--- + +### Toast + +- **File**: `magic_starter/lib/src/ui/components/toast/` +- **Class**: `Toast` +- **Recipe**: `WindRecipe` in `toast.recipe.dart` +- **Variants**: + - `tone`: `neutral` | `success` | `warning` | `destructive` +- **Token bindings**: + - `neutral`: `bg-surface border border-color-border text-fg` + - `success`: `bg-success text-on-primary` + - `warning`: `bg-warning text-on-primary` + - `destructive`: `bg-destructive text-on-destructive` +- **Anti-patterns**: + - Use for non-critical feedback only; critical errors belong in a dialog or inline error state. + - Auto-dismiss after 4-6 seconds unless action is required. + +--- + +### Tooltip + +- **File**: `magic_starter/lib/src/ui/components/tooltip/` +- **Class**: `Tooltip` +- **Recipe**: `WindSlotRecipe` in `tooltip.recipe.dart` +- **Slots**: `trigger`, `content` +- **Token bindings**: + - content: `bg-fg text-surface text-xs rounded-md px-2 py-1` +- **Anti-patterns**: + - Do not use tooltips for essential information; they are invisible on touch devices. + - WPopover real-click dismiss race is a known issue; do not add Tooltip to interactive paths that require precise tap timing. + +--- + +### DropdownMenu + +- **File**: `magic_starter/lib/src/ui/components/dropdown_menu/` +- **Class**: `DropdownMenu` +- **Recipe**: `WindSlotRecipe` in `dropdown_menu.recipe.dart` +- **Slots**: `trigger`, `panel`, `item`, `separator` +- **Token bindings**: + - panel: `bg-surface border border-color-border rounded-md shadow-sm` + - item: `text-sm text-fg hover:bg-surface-container-high` + - separator: `border-t border-color-border my-1` +- **Anti-patterns**: + - Do not use for primary navigation (use `Navbar` or `Tabs`). + - WPopover real-click dismiss race is a known issue; do not regress dismiss behavior. + +--- + +## Structure + +### FormField + +- **File**: `magic_starter/lib/src/ui/components/form_field/` +- **Class**: `FormField` (exported as `MagicFormField` to avoid collision with Flutter's `FormField`) +- **Recipe**: `WindSlotRecipe` in `form_field.recipe.dart` +- **Slots**: `root`, `label`, `hint`, `error` +- **Token bindings**: + - root: `flex flex-col gap-1` + - label: `text-sm font-medium text-fg` + - hint: `text-xs text-fg-muted` + - error: `text-xs text-destructive` +- **Anti-patterns**: + - Always wrap `Input`/`Textarea` in `MagicFormField`; never render label/error inline. + - Import as `MagicFormField` to avoid collision with Flutter's `FormField` widget. + +--- + +### PageHeader + +- **File**: `magic_starter/lib/src/ui/components/page_header/` +- **Class**: `PageHeader` +- **Recipe**: `WindSlotRecipe` in `page_header.recipe.dart` +- **Slots**: `title`, `subtitle`, `leading`, `actions`, `inlineActions` +- **Token bindings**: + - title: `text-xl font-bold text-fg` + - subtitle: `text-sm text-fg-muted` +- **Anti-patterns**: + - Do not add navigation chrome inside `PageHeader`; it is a content title, not an app bar. + +--- + +### EmptyState + +- **File**: `magic_starter/lib/src/ui/components/empty_state/` +- **Class**: `EmptyState` +- **Recipe**: `WindSlotRecipe` in `empty_state.recipe.dart` +- **Slots**: `root`, `iconWrap`, `title`, `description`, `action` +- **Token bindings**: + - iconWrap: `text-fg-disabled` + - title: `text-fg font-semibold text-lg` + - description: `text-fg-muted text-sm` +- **Anti-patterns**: + - Always include a call-to-action in the `action` slot; an empty state without an action is a dead end. + - Hide filters, tabs, or sorting controls that do not apply when the list is empty. + +--- + +### ErrorState + +- **File**: `magic_starter/lib/src/ui/components/error_state/` +- **Class**: `ErrorState` +- **Recipe**: `WindSlotRecipe` in `error_state.recipe.dart` +- **Slots**: `root`, `iconWrap`, `title`, `description`, `action` +- **Token bindings**: + - iconWrap: `text-destructive` + - title: `text-red-700 dark:text-red-400 font-semibold text-lg` + - description: `text-fg-muted text-sm` +- **Anti-patterns**: + - Use for unrecoverable states; for recoverable network errors, show a retry button in the `action` slot. + +--- + +### Navbar + +- **File**: `magic_starter/lib/src/ui/components/navbar/` +- **Class**: `Navbar` +- **Recipe**: `WindSlotRecipe` in `navbar.recipe.dart` +- **Slots**: `root`, `item`, `activeItem` +- **Token bindings**: + - root: `bg-surface border-t border-color-border` + - item inactive: `text-fg-muted` + - item active (`selected:`): `text-primary` +- **Anti-patterns**: + - Limit to 3-5 primary destinations. + - Do not place secondary actions in the bottom nav; use `DropdownMenu` or a settings page. + +--- + +## Composites + +### SocialDivider + +- **File**: `magic_starter/lib/src/ui/components/social_divider/` +- **Class**: `SocialDivider` +- **Token bindings**: `border-color-border text-fg-muted` +- **Anti-patterns**: + - Use only on auth screens to separate email login from social login options. + +--- + +### NotificationDropdown + +- **File**: Composite consuming `DropdownMenu` + `Badge` +- **Token bindings**: inherits from composites. +- **Anti-patterns**: + - Do not change the `StreamBuilder` unread-count subscription pattern; it is intentional. + +--- + +### UserProfileDropdown + +- **File**: Composite consuming `DropdownMenu` +- **Anti-patterns**: + - Do not add business logic to the dropdown; route to profile/settings views. + +--- + +### TeamSelector + +- **File**: Composite consuming `Select` or `DropdownMenu` +- **Anti-patterns**: + - Keep team-switch callback through `teamResolver`; do not hard-wire team ID. + +--- + +## Anti-patterns (global) + +| Anti-pattern | Category | Fix | +|-------------|----------|-----| +| Raw `Color(0xFF...)` or `Colors.*` in recipe or widget | Token violation | Use semantic alias (e.g. `bg-primary`) | +| Hardcoded pixel margin (`SizedBox(height: 13)`) | Spacing violation | Use Wind spacing utilities on the 4px scale | +| Multiple preview classes in one file | Preview structure | One `*.preview.dart` per component | +| Exporting preview class from `index.dart` | Preview boundary | `previews:refresh` discovers `*.preview.dart` directly | +| Importing `package:fluttersdk_wind/src/...` directly | Import convention | Use `package:magic/magic.dart` (re-exports wind) | +| Using Material `Switch`, `Checkbox`, `Radio`, `TabBar` | Primitive collision | Use the project component equivalents | +| CSS-only Wind utilities (`box-shadow`, `filter`, `transform`) | Wind unsupported | Use Flutter animation APIs | +| `Icons.*` inline in widget body | Tree-shaking | Extract as `static const IconData _icon = Icons.x;` | +| Missing `dark:` on any color token | Dark parity | Every alias expands to a light+dark pair | diff --git a/docs/design-culture/accessibility-wcag.md b/docs/design-culture/accessibility-wcag.md new file mode 100644 index 0000000..d914fe4 --- /dev/null +++ b/docs/design-culture/accessibility-wcag.md @@ -0,0 +1,201 @@ +# Accessibility / WCAG (Flutter/Wind reference) + +Read this for every screen. Accessibility is a constraint that applies regardless of design +language, and it is legally required in most markets. Target WCAG 2.2 Level AA. + +## What to target + +WCAG 2.2 AA is the baseline (EN 301 549 / EU Accessibility Act, ADA Title II). WCAG 3.0 is a +draft; do not design to APCA for compliance yet. + +## Contrast (hard numbers) + +- Body text: **4.5:1** against its background. +- Large text (>=24px, or >=18.66px bold): **3:1**. +- UI components and meaningful graphics (borders, icons, input outlines, focus rings): **3:1** + against adjacent color. +- Check BOTH light and dark independently; a token that passes in light can fail in dark. + +### design:lint enforcement + +`dart run bin/dispatcher.dart design:lint` enforces the 4.5:1 ratio on every `on-X`/`X` role +pair defined in DESIGN.md. It implements the WCAG relative-luminance formula (sRGB channel +linearization, 4.5:1 ratio check) against both the light and dark hex values. A lint failure +means the token pair does not pass for normal body text. + +The passing pairs in the current DESIGN.md: + +- `text-on-primary` (#FFFFFF) on `bg-primary` (#7C3AED light): 5.7:1 (pass) +- `text-on-destructive` (#FFFFFF) on `bg-destructive` (#DC2626 light): 4.8:1 (pass) + +If you update any color in DESIGN.md, re-run `design:lint` to verify the pair still passes in +both themes. + +## Touch targets + +- WCAG 2.5.8 (AA): interactive targets at least **24x24 logical px**, or enough spacing so a + 24px circle on each does not overlap a neighbor. +- Apple HIG and Material 3 ask for 44px and 48px respectively; those are the practical targets. +- In Wind className: `min-h-11` (44px) clears both Apple and the WCAG floor. Button `md`/`lg` + already meet this. Keep custom controls at `min-h-11`. + +## Focus visibility + +- WCAG 2.4.11 (AA): a focused element must not be fully hidden by sticky headers or overlays. + In Flutter: use `Scrollable.ensureVisible` or `ScrollController` to scroll focused elements + into view when a sticky header is present. +- Focus ring: at least 2px perimeter with 3:1 contrast against the unfocused state. +- In Wind className: `focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2` + provides a compliant focus ring. Apply it to every custom interactive widget. +- Never use a Flutter `FocusNode` that suppresses the default focus indicator without a + visible replacement. + +## Flutter Semantics + +Flutter does not use HTML; screen readers (TalkBack, VoiceOver) read the Semantics tree. Every +rule below maps the HTML/ARIA concept to Flutter. + +### Every interactive widget needs a Semantics label + +```dart +// Icon-only button +Semantics( + label: 'Close dialog', + button: true, + child: WButton(onPressed: onClose, child: Icon(Icons.close)), +) + +// Image with meaning +Semantics( + label: 'Profile photo for Jane Smith', + image: true, + child: CircleAvatar(backgroundImage: ...), +) + +// Decorative image: excludeSemantics: true +ExcludeSemantics(child: decorativeIcon) +``` + +### Heading hierarchy + +Use `Semantics(header: true)` on screen-level headings. Maintain one primary heading per screen. +Do not use heading markup for visual sizing; use `Typography` variants for visual size and +`Semantics(header: true)` for semantic role. + +```dart +Semantics( + header: true, + child: Typography(variant: TypographyVariant.headlineLg, text: 'Profile settings'), +) +``` + +### Form fields + +Every input must have a visible, persistent label. The `FormField` component handles label +association; do not rely on placeholder text as the label (it disappears on focus). + +Error messages must be text, not color alone. Use the `FormField` `errorText` parameter; it +renders red text AND associates it with the field via `Semantics(liveRegion: true)`. + +```dart +FormField( + label: 'Email address', + errorText: controller.errors['email'], + child: Input(form: form, name: 'email'), +) +``` + +### Live regions for dynamic updates + +Toast notifications and async status updates must announce themselves to screen readers: + +```dart +Semantics( + liveRegion: true, + child: Toast(message: 'Profile saved successfully.'), +) +``` + +For urgent alerts use `Semantics(liveRegion: true, namesRoute: false)` with a high-priority +announcement. Do not use live regions for every state change; only for content the user would +otherwise miss. + +### Navigation landmarks + +Wrap the main content area in `Semantics(explicitChildNodes: true)` to preserve tree structure. +The `AppLayout` shell handles landmark-level semantics; do not add redundant wrappers. + +## Color independence + +Never convey information by color alone (WCAG 1.4.1). Pair every color signal with text, an +icon, or a pattern: + +- An error input field gets `bg-destructive-container` border AND an error message text AND an + error icon. +- A success badge has `bg-success` background AND a checkmark icon AND a label. +- Active nav items use `bg-primary-container` AND bold weight AND an active indicator line. + +## Reduced motion + +Respect the OS "Reduce Motion" setting: + +```dart +// In a widget build method +final reduceMotion = MediaQuery.of(context).disableAnimations; + +// In Wind className: motion-safe: prefix gates the animation +WButton(className: 'transition-colors motion-safe:active:scale-95 ...') +``` + +Under reduced motion: disable parallax, large translates/scales, looping autoplay, staggered +reveals, and spinner animations. Keep or substitute: opacity fades, color transitions. Do not +delete motion that conveys state; substitute a static equivalent. + +No content flashes more than 3 times per second. Auto-playing motion over 5 seconds needs a +pause control. + +## Dragging and gestures + +Every drag interaction (sortable lists, sliders, swipe-to-dismiss) needs a single-pointer +non-drag alternative (WCAG 2.5.7 AA). For swipe-to-dismiss: also show a button or +long-press-menu option to trigger the same action. + +## Design-time checklist + +Before marking any screen done: + +1. Text contrast >=4.5:1 (3:1 large), verified in both light and dark via `design:lint`. +2. UI/border/focus-ring contrast >=3:1. +3. No color-only signals: every state has a text or icon companion. +4. Touch targets >=44px (min-h-11) for all interactive elements. +5. Visible focus ring (`focus-visible:ring-2 focus-visible:ring-primary`) on every interactive + widget; never obscured by sticky chrome. +6. One primary screen heading with `Semantics(header: true)`. +7. Every input has a visible label via `FormField`; errors are text + associated. +8. Decorative images have `ExcludeSemantics`; informative images have a `Semantics(label: ...)`. +9. All interactive elements keyboard/switch-accessible; no widget swallows focus without release. +10. Non-essential motion gated behind `motion-safe:` or `!MediaQuery.of(context).disableAnimations`. + +## How to apply in this codebase + +- Tokens pass WCAG: `text-fg` on `bg-surface`, `text-on-primary` on `bg-primary`, + `text-on-destructive` on `bg-destructive` are designed to pass. Re-check any custom pair. +- `FormField` component handles label association and error text automatically. +- `WButton`, `WInput`, `WAnchor` ship with a focus-ring className. Keep it; never strip it. +- Run `dart run bin/dispatcher.dart design:lint` after every DESIGN.md color change. +- Use `ExcludeSemantics` for decorative icons; `Semantics(label: ...)` for meaningful ones. + +## See also + +- [DESIGN.md](../DESIGN.md): color values; run design:lint to verify pairs +- [refactoring-ui.md](refactoring-ui.md): color independence, hierarchy, empty states +- [motion-interaction.md](motion-interaction.md): reduced-motion patterns +- [wind-responsive.md](wind-responsive.md): touch targets, SafeArea, hit-target sizing + +## Sources + +- W3C WAI: WCAG 2.2 Recommendation (1.4.1, 1.4.3, 1.4.11, 2.1.1, 2.4.3, 2.4.7, 2.4.11, 2.5.7, + 2.5.8, 3.3.1-3.3.3). +- Flutter documentation: Semantics, ExcludeSemantics, MediaQuery.disableAnimations, + FocusableActionDetector. +- EN 301 549 / EU Accessibility Act; ADA Title II. diff --git a/docs/design-culture/apple-hig.md b/docs/design-culture/apple-hig.md new file mode 100644 index 0000000..6caf2fa --- /dev/null +++ b/docs/design-culture/apple-hig.md @@ -0,0 +1,134 @@ +# Apple Human Interface Guidelines (Flutter/Wind reference) + +Read this before building a screen that should feel native on Apple platforms. It is a decision tool: +pick the language, then apply the rules. The guidance below is adapted from HIG principles to +Flutter/Wind/Magic idioms. + +## When to choose this language + +Choose Apple HIG when: + +- The audience is Apple-ecosystem-native and expects iOS/iPadOS patterns (bottom tab bar, swipe-back, edge gestures). +- The product is content-first: chrome should recede so content leads. +- Calm, restrained minimalism fits the brand better than loud expression. + +Avoid it when: + +- The product must reach Android parity equally (use material-design-3 instead). +- The domain is data-dense or tool-heavy; Material's explicit density fits better. +- The brand needs expressive, decorative visuals. + +## Core principles + +- **Clarity**: legible text at every size, precise icons, subtle adornment. +- **Deference**: the UI helps users interact with content without competing with it. +- **Depth**: layers communicate hierarchy via tonal elevation, not heavy drop shadows. + +## Layout and spacing rules + +- Every interactive element needs a minimum 44x44 logical-px hit target. On Flutter this means + `InkWell`, `GestureDetector`, or `WButton` with `min-h-11 min-w-11` in the className. Pad + invisibly rather than shrink the control. +- Use generous whitespace. Wind spacing follows the 4px logical scale; prefer `p-4`/`p-6`/`gap-4` + and resist `p-2` on content areas. +- On narrow (mobile) widths use `p-4` (16px) screen-edge margins; on tablet/desktop widths use `p-5` + (20px) or the layout shell's `md:px-5`. Do not reuse one margin across all breakpoints. +- Reflow vertically at narrow widths rather than truncate. +- Derive nested corner radii concentrically: inner radius = parent radius minus padding. In Wind + terms: a card with `rounded-lg` (16px) around content with 12px padding gets `rounded-md` (12px) + on a nested control, not an arbitrary value. + +In `AppLayout`, the `sm:` and `md:` breakpoint prefixes (`sm:flex-row`, `md:hidden`) handle the +compact-to-regular reflow. See [wind-responsive.md](wind-responsive.md) for the full breakpoint map. + +## Typography + +- Let type carry hierarchy: bolder, left-aligned section headings. Keep one font family; the + DESIGN.md typography font is authoritative (Inter by default in magic_example). +- Use the Typography component variants rather than arbitrary sizes: + `headline-lg` for page titles, `title-lg` for section heads, `body-lg`/`body-md` for content, + `label-md`/`label-sm` for interactive labels and captions. +- Body weight 400, headings and interactive labels weight 600-700. Never below 400 for small text. +- Left-align body text; never center multi-line prose. + +## Color and material + +- Use semantic tokens only. Never raw hex inside a component. + - Page canvas: `bg-surface` + - Cards and sheets: `bg-surface-container` + - Primary action: `bg-primary text-on-primary` + - Destructive: `bg-destructive text-on-destructive` + - Body text: `text-fg` + - Secondary text: `text-fg-muted` + - Hairlines: `border-color-border` + + The 17 semantic alias keys are defined in `DESIGN.md` and applied as wind aliases. Dark-mode + pairs are bundled in each token: `bg-surface` resolves to `bg-[#FFFFFF] dark:bg-[#030712]` + automatically. + +- Tint, do not repaint: apply `bg-primary` to primary actions only. Keep chrome neutral + (`bg-surface-container`, `text-fg`). +- In dark mode, elevation goes lighter (surface-container is lighter than surface). Never use + pure black; the `surface` dark token is `#030712`, an elevated dark gray. +- Reserve translucency for the navigation layer only. In Flutter, approximate a glass nav bar + with `BackdropFilter` + a semi-transparent surface. Never glass-on-glass, never glass in the + content layer. + +## Motion (Apple-specific feel) + +See [motion-interaction.md](motion-interaction.md) for Flutter easing/duration mechanics. + +Apple-specific feel: + +- Transitions originate from the element that triggered them (a sheet slides up from the + triggering button's area, not from a random edge). +- Use spring-based or ease-out curves; keep micro-interactions under 300ms. +- Animate state transitions that communicate hierarchy, not frequent interactions. +- Wrap non-essential animations with `motion-safe:` in Wind className, or guard with + `MediaQuery.of(context).disableAnimations` in Flutter widget code. + +## Accessibility + +- Contrast: 4.5:1 for normal text, 3:1 for large text and UI components, in BOTH light and dark. + `design:lint` enforces 4.5:1 on every `on-X`/`X` role pair. See + [accessibility-wcag.md](accessibility-wcag.md). +- Support text scaling: never hardcode font sizes for primary content. The Typography component + uses logical px; Flutter's `textScaleFactor` respects the OS setting automatically. +- Respect Reduce Motion: substitute a crossfade or opacity fade, do not delete animations. + Use `MediaQuery.of(context).disableAnimations` or the `motion-safe:` Wind prefix. +- Never use color as the only signal. Pair every color-only state with an icon or text label. +- Provide `Semantics` labels on icon-only controls: + ```dart + Semantics(label: 'Close', child: WButton(onPressed: ..., child: Icon(Icons.close))) + ``` + +## What makes it feel authentically Apple + +Reproduce: restraint (one primary surface per view), generous whitespace, crisp single-family +type, semantic adaptive color, subtle depth via tonal elevation. Avoid the tells of a cheap +imitation: raw hex, fixed text sizes, multiple typefaces, red used for non-destructive actions, +dense cramped layouts. + +## How to apply in this codebase + +1. Map the type scale to `Typography` component variants; Inter is the default app font. +2. Use semantic tokens exclusively in wind `className`: `bg-surface text-fg`, + `bg-primary text-on-primary`, `border-color-border`. +3. Dark mode is automatic: each token carries its `dark:` pair. Verify both themes pass contrast. +4. Honor 44px targets: Button `md`/`lg` already clear it; keep custom controls at `min-h-11`. +5. Keep radii concentric: `rounded-lg` (16px) outer card -> `rounded-md` (12px) nested controls. +6. Reserve `bg-destructive` for genuinely destructive actions, matching Apple's reserved-red rule. + +## See also + +- [DESIGN.md](../DESIGN.md): the 17 semantic token definitions and color values for this app +- [wind-responsive.md](wind-responsive.md): breakpoints, PageContainer, safe-area, sidebar vs bottom nav +- [accessibility-wcag.md](accessibility-wcag.md): contrast requirements and design:lint enforcement +- [motion-interaction.md](motion-interaction.md): easing, duration, and reduced-motion patterns in Flutter +- [material-design-3.md](material-design-3.md): M3 alternative when Material feel is preferred + +## Sources + +- Apple HIG: Design Principles, Layout, Typography, Color, Motion, Accessibility + (developer.apple.com/design/human-interface-guidelines). +- Flutter documentation: Semantics, MediaQuery.disableAnimations, TextScaleFactor. diff --git a/docs/design-culture/material-design-3.md b/docs/design-culture/material-design-3.md new file mode 100644 index 0000000..0ab4436 --- /dev/null +++ b/docs/design-culture/material-design-3.md @@ -0,0 +1,216 @@ +# Material Design 3 / Material You (Flutter/Wind reference) + +Read this before building a screen in Google's design language. It is a decision tool first, +spec second. This doc maps M3 concepts onto Flutter's `ThemeData`/`ColorScheme` and the Wind +semantic tokens defined in [DESIGN.md](../DESIGN.md). + +## When to choose this language + +Choose Material when: + +- Building for Android / Wear OS, or cross-platform apps that should feel at home on Google + devices. +- The domain is data-dense or tool-heavy (dashboards, forms, settings): Material's explicit + density and component set fit. +- You need robust light/dark plus accessible contrast without per-component tuning; the role + system gives it for free. +- The brand tolerates a colorful, rounded, springy aesthetic. + +Avoid it when: + +- You want a restrained, content-first Apple feel; see [apple-hig.md](apple-hig.md). + +## Core idea + +Material 3 is token-driven: components reference semantic ROLES, never raw hex. A single seed +color generates tonal palettes via the HCT color space; roles map specific tones to slots and +remap for dark mode automatically. + +In Flutter this is `ColorScheme.fromSeed(seedColor: ...)` plus `ThemeData.colorScheme`. In this +codebase, `design:sync` generates a `WindThemeData` from `DESIGN.md`; the violet `primary` +seed drives `toThemeData()` for Material interop. See `lib/config/` for the generated theme. + +## Mapping M3 roles to the 17 Wind semantic tokens + +The 17 semantic alias keys in DESIGN.md align to M3 roles. Use the Wind token in `className`; +Flutter's `ColorScheme` resolves the equivalent role for Material widget sub-trees. + +| M3 role | Wind token | Usage | +|---|---|---| +| `surface` | `bg-surface` | Page canvas | +| `surface-container` | `bg-surface-container` | Cards, sheets | +| `surface-container-high` | `bg-surface-container-high` | Input backgrounds, nested panels | +| `on-surface` | `text-fg` | Primary body text | +| `on-surface-variant` | `text-fg-muted` | Secondary/helper text | +| (disabled) | `text-fg-disabled` | Disabled labels | +| `primary` | `bg-primary` | Primary action fills | +| `on-primary` | `text-on-primary` | Text on primary fills | +| `primary-container` | `bg-primary-container` | Tonal button fills, badge backgrounds | +| (secondary accent) | `bg-accent` | Secondary emphasis fills | +| `outline` | `border-color-border` | Interactive borders, input outlines | +| `outline-variant` | `border-color-border-subtle` | Decorative dividers | +| `error` | `bg-destructive` | Error/danger fills | +| `on-error` | `text-on-destructive` | Text on error fills | +| `error-container` | `bg-destructive-container` | Error container backgrounds | +| (success) | `bg-success` | Success state fills | +| (warning) | `bg-warning` | Warning state fills | + +Always pair a role with its `on-*` token (for example `bg-primary text-on-primary`). Mixing +families breaks the guaranteed contrast. + +### Using tokens in Wind className + +```dart +// Primary action button +Button( + className: 'bg-primary text-on-primary rounded-md px-4 py-3', + child: Text('Save'), +) + +// Card surface +WCard( + className: 'bg-surface-container rounded-lg p-4 border border-color-border', + child: ..., +) + +// Secondary text +WText('Helper text', className: 'text-fg-muted text-sm') +``` + +Dark mode pairs are bundled: `bg-surface-container` resolves to +`bg-[#F9FAFB] dark:bg-[#111827]` automatically via wind aliases. + +## Surface depth system + +Express elevation with tonal surface-container steps, not drop shadows. + +``` +surface (#FFFFFF) <- page canvas + surface-container (#F9FAFB) <- cards, sheets + surface-container-high (#F3F4F6) <- input backgrounds, nested panels +``` + +Dark mode steps invert correctly: deeper grays for lower-elevation surfaces. Reserve shadows +(`shadow-sm`, `shadow-md`) for genuinely floating elements (popovers, modals). + +## State layers + +Hover, focus, and pressed states overlay the `on-*` color at low opacity: + +| State | Opacity | +|---|---| +| Hover | 8% | +| Focus | 10% | +| Pressed | 10% | +| Dragged | 16% | + +In Wind/magic_starter components this is expressed as `hover:bg-surface-container` or +`hover:opacity-90` rather than precise opacity math. Only one state layer at a time. + +## Navigation by breakpoint + +Material specifies navigation component by viewport width: + +- Under 600dp: bottom navigation bar (3-5 destinations) +- 600-839dp: navigation rail +- 840dp and above: navigation rail or navigation drawer + +In `AppLayout` (`magic_starter`) this maps to: + +- Mobile (below `md` breakpoint, 768px): bottom nav bar + hamburger drawer +- Desktop (`md:` and above): sidebar navigation rail + +Use `md:hidden` to show/hide between mobile and desktop layouts. See +[wind-responsive.md](wind-responsive.md) for the breakpoint map and layout patterns. + +## Typography + +The type scale below maps M3 roles to DESIGN.md typography variants. Use the `Typography` +component variant rather than specifying `text-*` sizes directly. + +| M3 role | Typography variant | Suggested className | +|---|---|---| +| Display Large | display | `font-bold` | +| Headline Large | headline-lg | `font-bold` | +| Headline Medium | headline-md | `font-semibold` | +| Title Large | title-lg | `font-semibold` | +| Body Large | body-lg | `font-normal` | +| Body Medium | body-md | `font-normal` | +| Label Medium | label-md | `font-semibold` | +| Label Small | label-sm | `font-medium` | + +Use weight 500-600 for interactive labels and tab titles, not Body weight. The DESIGN.md font +(Inter) is authoritative; do not override it per-component. + +## Shape scale + +| M3 name | Corner radius | Wind token | Use | +|---|---|---|---| +| Extra-small | 4px | `rounded-sm` | Chips, badges | +| Small | 8px | `rounded` | Inputs | +| Medium | 12px | `rounded-md` | Buttons | +| Large | 16px | `rounded-lg` | Cards, dialogs | +| Extra-large | 24px | `rounded-xl` | Bottom sheets | +| Full | 9999px | `rounded-full` | Pills, avatars | + +Use `rounded-lg` (16px) for cards and dialogs, `rounded-full` for pill badges, `rounded-md` +for buttons. Do not mix shape families without concentric nesting intent. + +## Component rules + +- One filled button per action group (the primary action). Secondary actions use tonal, outlined, + or text variants. Never two filled buttons side by side. +- Cards: `surface-container` background, `rounded-lg`, no shadow unless floating. Cards are + not navigation targets by themselves; wrap in a `GestureDetector` for interactivity. +- FAB or prominent action button: persists during scroll for the single most important action. + Not for destructive or rare actions. + +## Motion + +M3 uses spring-physics on Android/Compose. In Flutter, the closest equivalent is a +`CurvedAnimation` with `Curves.easeOutCubic` (entering) or `Curves.easeInCubic` (exiting). See +[motion-interaction.md](motion-interaction.md) for durations, reduced-motion patterns, and the +Wind `motion-safe:` prefix. + +M3 easing reference (use as curve approximations in Flutter `AnimationController`): + +- Entering: `emphasized-decelerate` ~ `cubic-bezier(0.05, 0.7, 0.1, 1)` -> `Curves.easeOutCubic` +- Exiting: `emphasized-accelerate` ~ `cubic-bezier(0.3, 0, 0.8, 0.15)` -> `Curves.easeInCubic` +- Standard: `cubic-bezier(0.2, 0, 0, 1)` -> `Curves.easeInOutCubic` + +## Accessibility + +- Touch targets: minimum 48x48dp (Material spec); WCAG floor is 24px. The Wind `min-h-11` + class (44px) meets both. Keep custom controls at `min-h-12` (48px) when targeting Material. +- The role system guarantees >=3:1 on `on-*` pairings for large text. Body text still needs + 4.5:1; `design:lint` enforces this on all `on-X`/`X` pairs. See + [accessibility-wcag.md](accessibility-wcag.md). +- Components must ship a visible focus ring. In Flutter use `FocusableActionDetector` or rely + on the `WButton`/`WInput` focus-ring className (`focus-visible:ring-2 focus-visible:ring-primary`). +- Provide `Semantics` for non-text content. Never use color alone. + +## How to apply in this codebase + +1. Use semantic tokens exclusively: never raw hex, never `Colors.*` constants in className. +2. Express depth by stepping `bg-surface` -> `bg-surface-container` -> `bg-surface-container-high`, + not by adding shadows. +3. Map the type scale to `Typography` component variants; weight 500-600 for interactive labels. +4. Keep radii from the shape scale; `rounded-lg` for cards, `rounded-md` for buttons, + `rounded-full` for pills. +5. For Material widget sub-trees (if used), the generated `ThemeData` from `design:sync` wires + the `ColorScheme` automatically. Do not override `ThemeData.colorScheme` by hand. + +## See also + +- [DESIGN.md](../DESIGN.md): the 17 semantic token definitions, hex values, and component tokens +- [wind-responsive.md](wind-responsive.md): breakpoints and navigation layout patterns +- [accessibility-wcag.md](accessibility-wcag.md): contrast requirements and design:lint enforcement +- [refactoring-ui.md](refactoring-ui.md): craft: hierarchy, spacing, type, color, and depth polish +- [motion-interaction.md](motion-interaction.md): easing, duration, and reduced-motion in Flutter + +## Sources + +- m3.material.io: color/roles, styles/typography, styles/shape, styles/motion/easing-and-duration, + components, foundations/accessible-design. +- Flutter documentation: ColorScheme.fromSeed, ThemeData, AnimationController, CurvedAnimation. +- material-foundation/material-color-utilities (HCT, tonal palette generation). diff --git a/docs/design-culture/motion-interaction.md b/docs/design-culture/motion-interaction.md new file mode 100644 index 0000000..4a35384 --- /dev/null +++ b/docs/design-culture/motion-interaction.md @@ -0,0 +1,214 @@ +# Motion and interaction (Flutter/Wind reference) + +Read this when adding any animation or interactive feedback to a screen. The 2026 consensus is +restraint: subtle, fast, purposeful. Motion you notice is usually wrong. + +## When to animate (and when not) + +Animate only for a reason: feedback (confirm an action), continuity (connect states), spatial +orientation (where did this come from), or guiding attention. Everything else is decoration; cut it. + +Frequency rule: the more often an action repeats, the less it should animate. + +| Frequency | Guideline | +|---|---| +| 100+ per day (toggles, tab switches) | No animation, instant | +| Tens per day (hover, nav item tap) | Minimal, <=150ms | +| Occasional (modals, drawers, toasts) | Standard animation | +| Rare / first-run (onboarding, empty state) | A little delight is allowed | + +## Easing + +In Flutter, easing maps to `Curve` values in `CurvedAnimation`: + +| Intent | Flutter Curve | Approximate bezier | +|---|---|---| +| Element entering | `Curves.easeOut` | cubic-bezier(0.23, 1, 0.32, 1) | +| Material entering | `Curves.easeOutCubic` | cubic-bezier(0.05, 0.7, 0.1, 1) | +| Moving / morphing | `Curves.easeInOut` | cubic-bezier(0.2, 0, 0, 1) | +| Element exiting | `Curves.easeIn` | acceptable ONLY for exits | +| Continuous (spinner) | `Curves.linear` | linear | + +Never use `Curves.easeIn` for entering UI; it delays movement exactly when the user is watching. + +## Durations + +Exit animations should be 20-30% faster than their enter counterpart. + +| Category | Duration | +|---|---| +| Micro (button press, tooltip, hover) | 100-200ms (press feedback 75-100ms) | +| Standard (dropdown, popover, select) | 200-300ms | +| Large (modal, drawer, route transition) | 300-500ms | +| Hard cap for UI feedback | 300ms | + +```dart +// Standard dropdown +AnimatedContainer( + duration: const Duration(milliseconds: 200), + curve: Curves.easeOut, + ... +) + +// Large modal enter +PageRouteBuilder( + transitionDuration: const Duration(milliseconds: 350), + ... +) +``` + +## Micro-interactions in Flutter + +Every interactive element has six states: default, hover (desktop), focused, pressed, disabled, +loading. A missing loading or disabled state is the most common quality gap. + +In Wind `className`, the state prefixes are: + +```dart +WButton( + className: ''' + transition-colors duration-100 + hover:bg-primary-container + focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 + active:opacity-90 + disabled:opacity-50 + motion-safe:active:scale-95 + ''', + ... +) +``` + +- Hover: color/opacity shift, 100-150ms. On mobile there is no hover; `WAnchor` gates hover + behind pointer-device detection automatically. +- Press: `motion-safe:active:scale-95` at ~75ms for tactile feedback. Gate with `motion-safe:` + so it does not fire under reduced motion. +- Focus: `focus-visible:ring-2 focus-visible:ring-primary`; keyboard only (never bare `:focus`); + never removed. +- Disabled: `disabled:opacity-50`. Also set `onPressed: null` to disable the GestureDetector. +- Loading: show a loading spinner or skeleton; do not disable without visual feedback. + +## Overlays and transitions in Flutter + +For modals, bottom sheets, and drawers, Flutter's built-in route system handles the animation +curve. Customize via `PageRouteBuilder` or `showModalBottomSheet` parameters: + +```dart +// Bottom sheet: slide up from bottom, ease-out +showModalBottomSheet( + context: context, + isScrollControlled: true, + transitionAnimationController: AnimationController( + duration: const Duration(milliseconds: 300), + vsync: this, + ), + builder: (_) => BottomSheetContent(), +) +``` + +For in-page expand/collapse (accordion, inline panel): + +```dart +AnimatedSize( + duration: const Duration(milliseconds: 200), + curve: Curves.easeOut, + child: isExpanded ? ExpandedContent() : const SizedBox.shrink(), +) +``` + +Pattern: opacity + small directional slide toward the trigger gives spatial context. Keep +tooltip/dropdown entrances 100-150ms. + +## Route transitions + +In magic_example the router is `go_router`. Route transitions use `CustomTransitionPage`: + +```dart +CustomTransitionPage( + child: const DashboardView(), + transitionsBuilder: (context, animation, secondaryAnimation, child) { + return FadeTransition(opacity: animation, child: child); + }, + transitionDuration: const Duration(milliseconds: 250), +) +``` + +Auth routes use `RouteTransition.none` (no animation between auth screens). For content +screens a simple fade (150-250ms, `Curves.easeOut`) is the safest default. + +## Loading and skeleton states + +- Show a `Skeleton` component within ~300ms if data has not arrived. +- Use `Skeleton(shape: SkeletonShape.block)` for card-shaped areas, `SkeletonShape.text` for + text lines, `SkeletonShape.circle` for avatars. +- Reserve spinners for short blocking mutations (submit, auth). +- Gate the shimmer pulse animation behind `motion-safe:`: + +```dart +Skeleton( + className: 'motion-safe:animate-pulse bg-surface-container-high rounded', +) +``` + +## Performance in Flutter + +- Animate ONLY properties handled by the compositor: `opacity`, `transform` (via `Transform` or + `AnimatedContainer`). Avoid animating `width`, `height`, `padding`, or `margin` (layout pass + every frame). +- Use `RepaintBoundary` around complex animated subtrees to isolate repaints. +- Avoid many simultaneous animations in one viewport. Use `ListView.builder` or + `SliverList` for long off-screen lists; Flutter will tree-shake non-visible widgets. +- `TickerProviderStateMixin` properly disposes controllers; always call `controller.dispose()` + in `State.dispose()`. + +## Accessibility and restraint + +Respect the OS "Reduce Motion" setting via `MediaQuery.of(context).disableAnimations`: + +```dart +final reduceMotion = MediaQuery.of(context).disableAnimations; + +// Wind className: motion-safe: prefix gates the animation token +WButton(className: 'motion-safe:active:scale-95 ...') + +// Dart animation controller: skip or instant when reduced +controller.duration = reduceMotion + ? Duration.zero + : const Duration(milliseconds: 200); +``` + +Disable under reduced motion: parallax, scale/zoom, large pan/translate, looping autoplay, +staggered reveals, shimmer. Keep or substitute: opacity fades, color transitions, instant snap. +Substitute, do not just delete, motion that conveys state (for example, replace a spinner with a +static icon when motion is disabled, do not hide loading state entirely). + +No content flashes more than 3 times per second. Auto-playing motion over 5 seconds needs a +pause control. + +## Per-screen motion checklist + +Before marking any screen done: + +1. Every animated element guards against reduced motion via `motion-safe:` or + `MediaQuery.of(context).disableAnimations`. +2. Only `opacity` and `transform` animate; no layout-triggering properties. +3. Entrances `Curves.easeOut`; exits faster; no `Curves.easeIn` on entrances. +4. UI feedback under 200ms; large transitions under 500ms. +5. Six interactive states present (default, hover, focus, active, disabled, loading). +6. Auto-play is controllable; nothing flashes >3 times/sec. + +## See also + +- [DESIGN.md](../DESIGN.md): brand personality and DESIGN.md motion direction +- [accessibility-wcag.md](accessibility-wcag.md): WCAG 2.2.2 / 2.3.1 reduced-motion requirements +- [wind-responsive.md](wind-responsive.md): safe-area, layout transitions +- [apple-hig.md](apple-hig.md): Apple spring-based motion feel +- [material-design-3.md](material-design-3.md): M3 easing tokens + +## Sources + +- Emil Kowalski / animations.dev (easing + duration tables, frequency rule). +- Material Design 3 motion: easing/duration tokens (m3.material.io/styles/motion). +- Apple HIG motion (developer.apple.com); WWDC23 "Animate with springs". +- Flutter documentation: CurvedAnimation, AnimationController, AnimatedContainer, AnimatedSize, + MediaQuery.disableAnimations, RepaintBoundary. +- WCAG 2.2: 2.2.2 / 2.3.1 / 2.3.3. diff --git a/docs/design-culture/refactoring-ui.md b/docs/design-culture/refactoring-ui.md new file mode 100644 index 0000000..2047708 --- /dev/null +++ b/docs/design-culture/refactoring-ui.md @@ -0,0 +1,185 @@ +# Refactoring UI craft (Flutter/Wind reference) + +Read this for every screen, regardless of design language. Apple/Material decide the visual +identity; this decides whether the result looks designed or amateur. These are defaults to apply, +not options to choose. + +Source: "Refactoring UI" (Wathan and Schoger) adapted to Flutter/Wind/Magic idioms. + +## Hierarchy (the highest-leverage skill) + +Not all elements are equal. Give every screen one clear primary action; demote the rest. Build a +pyramid: one primary, a few secondary, the rest tertiary. + +Emphasize by de-emphasizing. To make the primary stand out, soften the secondary; do not just +enlarge the primary. + +Use the levers in this order: color/contrast first, font-weight second, size last. Size alone is +a weak signal. + +- Cap variety: 2-3 font weights and 2-3 text-contrast levels per screen. More tiers read as noise. +- For secondary text use lower contrast, not smaller size. Use `text-fg-muted` rather than + dropping the font size. +- For labels, prefer format and context over a label ("12 left in stock", not "Stock: 12"). +- Style by visual role, not widget type. A heading can be small; a button text can be quiet. + +### Applying hierarchy with Wind tokens + +```dart +// Primary action: high contrast, filled +Button(variant: ButtonVariant.primary, child: Text('Save changes')) + +// Secondary: outlined or ghost +Button(variant: ButtonVariant.secondary, child: Text('Cancel')) + +// Destructive: reserve bg-destructive +Button(variant: ButtonVariant.destructive, child: Text('Delete account')) + +// Primary body text +WText('Your profile has been updated.', className: 'text-fg text-base') + +// Secondary helper text +WText('Changes take effect immediately.', className: 'text-fg-muted text-sm') +``` + +## Anti-AI-slop tells + +The following patterns are how generated or rushed UI gives itself away. Avoid all of them. + +- **Purple-on-gray everything**: primary color leaked onto every surface. Reserve `bg-primary` + for the one primary action per view. Everything else is neutral. +- **All text the same size and weight**: no hierarchy. Use `Typography` variants; vary weight + before varying size. +- **Cards with too much padding and no content**: a card that is mostly whitespace with one short + line of text. Cards need meaningful content density. +- **Every section the same vertical rhythm**: no breathing room variation between a dense form + and a spacious hero. Use the spacing scale deliberately. +- **Placeholder content left in production**: "Lorem ipsum", "User Name", "Coming soon" states + with no real data shape. Design with realistic data from the start. +- **No empty state designed**: lists and feeds without a "no data" state. Every list needs a + designed empty state (icon + title + one clear CTA). +- **Icon-only buttons with no label and no Semantics**: inaccessible and confusing. Add a + `Semantics(label: ...)` wrapper or a visible label. +- **Shadows on everything**: depth without meaning. Reserve `shadow-sm` for cards/inputs, + `shadow-md` for dropdowns, `shadow-lg` for modals. Do not stack. +- **Flat gray on gray**: a form input on a surface that is the same `bg-surface` color. Use + `bg-surface-container-high` for input backgrounds to give the minimal contrast needed. + +## Spacing and layout + +- Start with too much whitespace, then remove. Under-spacing is the more common failure. +- Enforce the relationship rule: space INSIDE a group < space BETWEEN groups < space around a + section. Proximity is how users perceive grouping. +- Use the Wind 4px logical scale only: `p-1`(4px) `p-2`(8px) `p-3`(12px) `p-4`(16px) + `p-6`(24px) `p-8`(32px) `p-12`(48px) `p-16`(64px). If a value is not on the scale, snap to + the nearest step; never invent an arbitrary offset. +- Do not fill available width. Constrain text columns via a `ConstrainedBox` with + `maxWidth: 600` (equivalent to `max-w-prose`). Use `PageContainer` for page shells. See + [wind-responsive.md](wind-responsive.md). +- Dense data UIs (tables, dashboards) use a compressed scale (drop one or two steps), not a + different arbitrary set. + +### Spacing in practice + +```dart +// Section separation: gap-6 (24px) between sections +WDiv( + className: 'flex flex-col gap-6 p-4', + children: [ + SectionHeader(), + FormContent(), // gap between items inside: gap-4 + ActionRow(), + ], +) + +// Group separation: gap-4 (16px) between form fields +WDiv( + className: 'flex flex-col gap-4', + children: [nameField, emailField, passwordField], +) +``` + +## Typography + +- Use `Typography` component variants rather than raw `text-*` sizes. The variants are calibrated + to a modular scale from the DESIGN.md font definitions. +- The DESIGN.md font (Inter by default) is authoritative. Do not add a second typeface per-component. +- Weight: `font-normal` (400) for body, `font-medium` (500) for UI labels, `font-semibold` (600) + for headings and primary actions. Never below 400 for body or small text. +- Line-height is inverse to size: body `leading-relaxed` (1.5-1.6), UI components `leading-tight` + (1.2-1.3), headings `leading-snug` (1.1-1.2). +- Left-align body; never center multi-line prose. Right-align numeric columns. +- Letter-spacing: leave body at the font default. For all-caps labels add `tracking-wide` + (`+0.05em`). For large display headings add `tracking-tight` (`-0.01em`). + +## Color + +- Define the full palette up front in DESIGN.md; never invent a shade at use-time. +- Consume semantic tokens; never hardcode hex inside a component. The 17 token names are defined + in [DESIGN.md](../DESIGN.md). +- Never put neutral `text-fg-muted` on a colored surface. Use the matching `on-*` token instead + (`text-on-primary` on `bg-primary`, `text-on-destructive` on `bg-destructive`). +- Use saturated color sparingly, for the one thing that must stand out. Most hierarchy comes from + contrast, weight, and spacing. +- Never rely on color alone for state: pair with an icon or label. See + [accessibility-wcag.md](accessibility-wcag.md). + +## Depth and finishing polish + +- Light comes from above: if using shadows, they fall downward. Assign shadows by role: + - Cards, inputs: `shadow-sm` + - Dropdowns, tooltips: `shadow-md` + - Modals, popovers: `shadow-lg` +- Design grayscale-first. If the hierarchy reads without color, color reinforces it; if it only + works with color, fix spacing/contrast first. +- Use fewer borders. Separate with a background-contrast shift (`bg-surface` vs + `bg-surface-container`), a subtle shadow, or extra spacing before reaching for + `border-color-border`. +- Add finishing touches: + - Accent borders: a colored left strip on cards or alerts (`border-l-2 border-primary`). + - Designed empty states: icon + title + one clear CTA. Never ship a bare "No results". + - Designed loading states: `Skeleton` component matching the real layout shape. Do not + show a spinner where structure is known. + +### Empty state example + +```dart +EmptyState( + icon: Icons.notifications_none, + title: 'No notifications yet', + description: 'You will see updates here when activity happens.', + action: Button( + variant: ButtonVariant.secondary, + child: Text('Refresh'), + onPressed: controller.refresh, + ), +) +``` + +## How to apply in this codebase + +- Hierarchy: use `Typography` variants + `font-semibold` and `text-fg` vs `text-fg-muted` for + contrast tiers. Reserve `bg-primary` for the single primary action per view; secondary actions + use Button `secondary` or `ghost` variant. +- Spacing: use the 4px Wind scale (`gap-4`/`p-4`/`gap-6`) and keep inside < between < section. + Wrap text columns in a `ConstrainedBox`; use `PageContainer` for page shells. +- Color: consume the 17 semantic tokens from DESIGN.md. On a colored surface use the matching + `on-*` token; never use `text-fg-muted` on a filled background. +- Depth: `rounded-lg` for cards/dialogs, `rounded-md` for buttons. Prefer + `border-color-border` only where a contrast shift will not do. +- Polish: build the mockup with realistic data first. Always design the empty and loading state + of any list or async surface. + +## See also + +- [DESIGN.md](../DESIGN.md): token definitions, typography scale, spacing scale +- [accessibility-wcag.md](accessibility-wcag.md): contrast requirements +- [material-design-3.md](material-design-3.md): M3 role-to-token mapping +- [wind-responsive.md](wind-responsive.md): layout patterns and PageContainer +- [motion-interaction.md](motion-interaction.md): loading states, skeleton, transition patterns + +## Sources + +- "Refactoring UI" by Adam Wathan and Steve Schoger (refactoringui.com). +- Flutter documentation: ConstrainedBox, TextStyle, MediaQuery. +- Wind token documentation: semantic aliases, 4px spacing scale. diff --git a/docs/design-culture/wind-responsive.md b/docs/design-culture/wind-responsive.md new file mode 100644 index 0000000..4957682 --- /dev/null +++ b/docs/design-culture/wind-responsive.md @@ -0,0 +1,263 @@ +# Wind responsive layout (Flutter reference) + +Read this when building any screen layout. Wind's responsive system is breakpoint-prefix-driven, +not CSS media-query-driven. This doc covers breakpoints, page containers, safe-area handling, +navigation layout patterns, hit targets, and mobile-first rules. + +## Breakpoints + +Wind uses three responsive prefixes. They apply to the logical viewport width reported by +`MediaQuery.of(context).size.width`: + +| Prefix | Min width | Typical device | +|---|---|---| +| (none, default) | 0px | All screens, mobile first | +| `sm:` | 480px | Large phones, landscape | +| `md:` | 768px | Tablets, desktop | +| `lg:` | 1024px | Wide desktop | + +Breakpoints are mobile-first: a class without a prefix applies to ALL widths; a prefixed class +overrides from that breakpoint up. + +```dart +// Single-column on mobile, two-column on md+ +WDiv( + className: 'flex flex-col md:flex-row gap-4', + children: [MainContent(), Sidebar()], +) +``` + +Wind has no CSS `@media` query mechanism; the breakpoint resolution runs inside the Wind parser +using the current `MediaQuery` width. Do not try to use CSS media queries or `LayoutBuilder` +directly for layout decisions that are already expressible as Wind breakpoint prefixes. + +## PageContainer + +`PageContainer` (from magic_starter) constrains the content width and applies consistent +horizontal gutters. Always wrap page-level content in `PageContainer`; do not build custom max- +width constraints per-screen. + +```dart +PageContainer( + child: WDiv( + className: 'flex flex-col gap-6 py-6', + children: [...], + ), +) +``` + +`PageContainer` sets `maxWidth: 1200px` and the horizontal padding: + +- Mobile: `px-4` (16px each side) +- `md:` and above: `px-6` (24px each side) + +Do not add extra horizontal padding inside `PageContainer`; let it handle the gutters. + +## Safe area handling + +On iOS and Android, system UI (status bar, home indicator, notch) can overlap content. Always +wrap the root of a full-screen page in `SafeArea`: + +```dart +// In a page-level build method +Scaffold( + body: SafeArea( + child: PageContainer(child: content), + ), +) +``` + +`AppLayout` and `GuestLayout` apply `SafeArea` at the shell level; views rendered inside the +layout shell do NOT need to re-wrap in `SafeArea`. + +For custom bottom actions (a sticky CTA at the bottom of the screen), account for the home +indicator via `MediaQuery.viewPaddingOf(context).bottom`: + +```dart +Padding( + padding: EdgeInsets.only( + bottom: MediaQuery.viewPaddingOf(context).bottom + 16, + ), + child: ActionButton(), +) +``` + +Dialog safe area uses the modal formula: `safeHeight = screenHeight - top - bottom insets`, +then `maxHeight = safeHeight * 0.85`. The `Dialog` and `BottomSheet` components in magic_starter +handle this automatically via `MediaQuery.viewPaddingOf(context)`. + +## Navigation: sidebar vs bottom nav + +The `AppLayout` shell switches navigation patterns by breakpoint: + +| Breakpoint | Navigation pattern | +|---|---| +| Below `md:` (mobile) | Bottom navigation bar + hamburger drawer | +| `md:` and above | Sidebar navigation rail | + +To implement this in a custom layout follow the `AppLayout` pattern: + +```dart +// Show sidebar on desktop, hide on mobile +WDiv( + className: 'hidden md:flex flex-col w-64 bg-surface-container border-r border-color-border', + children: [SidebarContent()], +) + +// Show bottom nav on mobile, hide on desktop +WDiv( + className: 'flex md:hidden flex-row bg-surface-container border-t border-color-border', + children: [BottomNavItems()], +) +``` + +Never show both a sidebar and a bottom nav at the same breakpoint. On mobile, the sidebar +appears as a drawer (use `Drawer` + `Scaffold.drawer`). + +## 44px hit targets + +Every interactive element must have a minimum 44x44 logical-px hit target (Apple HIG / Material +recommendation; WCAG 2.5.8 floor is 24px). + +In Wind className: `min-h-11` equals 44px logical height. The default Button `md`/`lg` sizes +already clear this. + +For icon-only controls (icon buttons, nav items, close buttons): + +```dart +// Ensure 44px hit target on a small icon +SizedBox( + width: 44, + height: 44, + child: Center( + child: Icon(Icons.close, size: 20), + ), +) +``` + +Or with Wind: `WButton(className: 'min-h-11 min-w-11 p-0 flex items-center justify-center', ...)` + +Bottom nav items must be at least 44px tall. Sidebar nav items should be at least 44px tall with +sufficient horizontal padding (`py-2 px-3` minimum on a nav item gives ~40px; add `min-h-11` to +guarantee it). + +## Mobile-first composition rules + +1. Start with the mobile layout (no prefix). Confirm it reads on a 375px width. +2. Add `md:` overrides for tablet/desktop changes (column -> row, hidden -> shown, etc.). +3. Add `lg:` overrides only when desktop needs a third layout step. +4. Never write `sm:hidden` or `sm:flex` as the primary display rule; start mobile-visible, then + hide at breakpoints. + +```dart +// Correct: visible by default, hidden on desktop +WDiv(className: 'flex md:hidden', children: [MobileMenu()]) + +// Correct: hidden by default (inline), visible on desktop +WDiv(className: 'hidden md:flex', children: [Sidebar()]) +``` + +## Text and content constraints + +Do not fill available width with text. Constrain reading columns: + +```dart +ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 600), + child: WText(longBodyText, className: 'text-fg body-md'), +) +``` + +Inside `PageContainer`, text naturally hits the container max-width. For individual narrow +columns (auth forms, settings cards) apply an inner `ConstrainedBox(maxWidth: 480)`. + +## WindRecipe with responsive variants + +When a component has a layout-responsive variant, express it in the recipe's `className` caller +rather than as a recipe variant axis. Responsive breakpoints are caller context, not component +internals: + +```dart +// Caller supplies the responsive layout via className +Card( + className: 'w-full md:w-1/2 lg:w-1/3', + child: ..., +) +``` + +The `WindRecipe` base and variant tokens handle visual state (tone, size, intent). Responsive +width is caller responsibility. + +## Platform prefixes + +Wind provides platform prefixes for platform-specific styles: + +| Prefix | Platform | +|---|---| +| `ios:` | iOS | +| `android:` | Android | +| `web:` | Flutter Web | +| `mobile:` | iOS + Android | +| `macos:` | macOS | + +Use these sparingly for platform-specific affordances (for example, `ios:rounded-xl` to apply +a rounder corner on iOS only). Do not use them as a substitute for breakpoint-based layout. + +## Common layout patterns + +### Full-screen auth page (GuestLayout shell) + +```dart +// GuestLayout constrains to 480px max-width, centered, scrollable +// Views inside it use: +WDiv( + className: 'flex flex-col gap-6 px-4 py-8', + children: [ + Logo(), + Card(child: LoginForm()), + SocialDivider(), + SocialButtons(), + ], +) +``` + +### Dashboard with sidebar (AppLayout shell on desktop) + +```dart +// AppLayout provides the sidebar rail on md+; the view sees only the content slot +WDiv( + className: 'flex flex-col gap-6 p-6', + children: [ + PageHeader(title: 'Dashboard'), + StatsGrid(), + RecentActivity(), + ], +) +``` + +### Responsive two-column form + +```dart +WDiv( + className: 'grid grid-cols-1 md:grid-cols-2 gap-4', + children: [ + FormField(label: 'First name', child: Input(...)), + FormField(label: 'Last name', child: Input(...)), + ], +) +``` + +## See also + +- [DESIGN.md](../DESIGN.md): spacing scale (4px logical grid), gutter/section values +- [accessibility-wcag.md](accessibility-wcag.md): 44px hit targets, SafeArea requirements +- [material-design-3.md](material-design-3.md): navigation by breakpoint (M3 guidance) +- [apple-hig.md](apple-hig.md): screen-edge margins, concentric radii +- [refactoring-ui.md](refactoring-ui.md): whitespace, text column constraints + +## Sources + +- magic_starter `AppLayout` and `GuestLayout` source (`lib/src/ui/layouts/`). +- Wind breakpoint prefix documentation (`wind/CLAUDE.md`). +- Flutter documentation: MediaQuery, SafeArea, Scaffold.drawer, ConstrainedBox. +- Apple HIG Layout guidelines; Material 3 navigation components. 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..b77a925 --- /dev/null +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,647 @@ +// !$*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 */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; + 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 = ""; }; + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; 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 = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C8082294A63A400263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C807B294A618700263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */, + 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; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); + 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; + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */, + ); + 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)"; + DEVELOPMENT_TEAM = 883V9SVA54; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.fluttersdk.magicExample; + 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.fluttersdk.magicExample.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.fluttersdk.magicExample.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.fluttersdk.magicExample.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)"; + DEVELOPMENT_TEAM = 883V9SVA54; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.fluttersdk.magicExample; + 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)"; + DEVELOPMENT_TEAM = 883V9SVA54; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.fluttersdk.magicExample; + 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 */ + +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency 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..c3fedb2 --- /dev/null +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 zcmeHN2~<R zh`|8`A_PQ1nSu(UMFx?8j8PC!!VDphaL#`F42fd#7Vlc`zIE4n%Y~eiz4y1j|NDpi z?<@|pSJ-HM`qifhf@m%MamgwK83`XpBA<+azdF#2QsT{X@z0A9Bq>~TVErigKH1~P zRX-!h-f0NJ4Mh++{D}J+K>~~rq}d%o%+4dogzXp7RxX4C>Km5XEI|PAFDmo;DFm6G zzjVoB`@qW98Yl0Kvc-9w09^PrsobmG*Eju^=3f?0o-t$U)TL1B3;sZ^!++3&bGZ!o-*6w?;oOhf z=A+Qb$scV5!RbG+&2S}BQ6YH!FKb0``VVX~T$dzzeSZ$&9=X$3)_7Z{SspSYJ!lGE z7yig_41zpQ)%5dr4ff0rh$@ky3-JLRk&DK)NEIHecf9c*?Z1bUB4%pZjQ7hD!A0r-@NF(^WKdr(LXj|=UE7?gBYGgGQV zidf2`ZT@pzXf7}!NH4q(0IMcxsUGDih(0{kRSez&z?CFA0RVXsVFw3^u=^KMtt95q z43q$b*6#uQDLoiCAF_{RFc{!H^moH_cmll#Fc^KXi{9GDl{>%+3qyfOE5;Zq|6#Hb zp^#1G+z^AXfRKaa9HK;%b3Ux~U@q?xg<2DXP%6k!3E)PA<#4$ui8eDy5|9hA5&{?v z(-;*1%(1~-NTQ`Is1_MGdQ{+i*ccd96ab$R$T3=% zw_KuNF@vI!A>>Y_2pl9L{9h1-C6H8<)J4gKI6{WzGBi<@u3P6hNsXG=bRq5c+z;Gc3VUCe;LIIFDmQAGy+=mRyF++u=drBWV8-^>0yE9N&*05XHZpPlE zxu@?8(ZNy7rm?|<+UNe0Vs6&o?l`Pt>P&WaL~M&#Eh%`rg@Mbb)J&@DA-wheQ>hRV z<(XhigZAT z>=M;URcdCaiO3d^?H<^EiEMDV+7HsTiOhoaMX%P65E<(5xMPJKxf!0u>U~uVqnPN7T!X!o@_gs3Ct1 zlZ_$5QXP4{Aj645wG_SNT&6m|O6~Tsl$q?nK*)(`{J4b=(yb^nOATtF1_aS978$x3 zx>Q@s4i3~IT*+l{@dx~Hst21fR*+5}S1@cf>&8*uLw-0^zK(+OpW?cS-YG1QBZ5q! zgTAgivzoF#`cSz&HL>Ti!!v#?36I1*l^mkrx7Y|K6L#n!-~5=d3;K<;Zqi|gpNUn_ z_^GaQDEQ*jfzh;`j&KXb66fWEk1K7vxQIMQ_#Wu_%3 z4Oeb7FJ`8I>Px;^S?)}2+4D_83gHEq>8qSQY0PVP?o)zAv3K~;R$fnwTmI-=ZLK`= zTm+0h*e+Yfr(IlH3i7gUclNH^!MU>id$Jw>O?2i0Cila#v|twub21@e{S2v}8Z13( zNDrTXZVgris|qYm<0NU(tAPouG!QF4ZNpZPkX~{tVf8xY690JqY1NVdiTtW+NqyRP zZ&;T0ikb8V{wxmFhlLTQ&?OP7 z;(z*<+?J2~z*6asSe7h`$8~Se(@t(#%?BGLVs$p``;CyvcT?7Y!{tIPva$LxCQ&4W z6v#F*);|RXvI%qnoOY&i4S*EL&h%hP3O zLsrFZhv&Hu5tF$Lx!8(hs&?!Kx5&L(fdu}UI5d*wn~A`nPUhG&Rv z2#ixiJdhSF-K2tpVL=)5UkXRuPAFrEW}7mW=uAmtVQ&pGE-&az6@#-(Te^n*lrH^m@X-ftVcwO_#7{WI)5v(?>uC9GG{lcGXYJ~Q8q zbMFl7;t+kV;|;KkBW2!P_o%Czhw&Q(nXlxK9ak&6r5t_KH8#1Mr-*0}2h8R9XNkr zto5-b7P_auqTJb(TJlmJ9xreA=6d=d)CVbYP-r4$hDn5|TIhB>SReMfh&OVLkMk-T zYf%$taLF0OqYF?V{+6Xkn>iX@TuqQ?&cN6UjC9YF&%q{Ut3zv{U2)~$>-3;Dp)*(? zg*$mu8^i=-e#acaj*T$pNowo{xiGEk$%DusaQiS!KjJH96XZ-hXv+jk%ard#fu=@Q z$AM)YWvE^{%tDfK%nD49=PI|wYu}lYVbB#a7wtN^Nml@CE@{Gv7+jo{_V?I*jkdLD zJE|jfdrmVbkfS>rN*+`#l%ZUi5_bMS<>=MBDNlpiSb_tAF|Zy`K7kcp@|d?yaTmB^ zo?(vg;B$vxS|SszusORgDg-*Uitzdi{dUV+glA~R8V(?`3GZIl^egW{a919!j#>f` znL1o_^-b`}xnU0+~KIFLQ)$Q6#ym%)(GYC`^XM*{g zv3AM5$+TtDRs%`2TyR^$(hqE7Y1b&`Jd6dS6B#hDVbJlUXcG3y*439D8MrK!2D~6gn>UD4Imctb z+IvAt0iaW73Iq$K?4}H`7wq6YkTMm`tcktXgK0lKPmh=>h+l}Y+pDtvHnG>uqBA)l zAH6BV4F}v$(o$8Gfo*PB>IuaY1*^*`OTx4|hM8jZ?B6HY;F6p4{`OcZZ(us-RVwDx zUzJrCQlp@mz1ZFiSZ*$yX3c_#h9J;yBE$2g%xjmGF4ca z&yL`nGVs!Zxsh^j6i%$a*I3ZD2SoNT`{D%mU=LKaEwbN(_J5%i-6Va?@*>=3(dQy` zOv%$_9lcy9+(t>qohkuU4r_P=R^6ME+wFu&LA9tw9RA?azGhjrVJKy&8=*qZT5Dr8g--d+S8zAyJ$1HlW3Olryt`yE zFIph~Z6oF&o64rw{>lgZISC6p^CBer9C5G6yq%?8tC+)7*d+ib^?fU!JRFxynRLEZ zj;?PwtS}Ao#9whV@KEmwQgM0TVP{hs>dg(1*DiMUOKHdQGIqa0`yZnHk9mtbPfoLx zo;^V6pKUJ!5#n`w2D&381#5#_t}AlTGEgDz$^;u;-vxDN?^#5!zN9ngytY@oTv!nc zp1Xn8uR$1Z;7vY`-<*?DfPHB;x|GUi_fI9@I9SVRv1)qETbNU_8{5U|(>Du84qP#7 z*l9Y$SgA&wGbj>R1YeT9vYjZuC@|{rajTL0f%N@>3$DFU=`lSPl=Iv;EjuGjBa$Gw zHD-;%YOE@<-!7-Mn`0WuO3oWuL6tB2cpPw~Nvuj|KM@))ixuDK`9;jGMe2d)7gHin zS<>k@!x;!TJEc#HdL#RF(`|4W+H88d4V%zlh(7#{q2d0OQX9*FW^`^_<3r$kabWAB z$9BONo5}*(%kx zOXi-yM_cmB3>inPpI~)duvZykJ@^^aWzQ=eQ&STUa}2uT@lV&WoRzkUoE`rR0)`=l zFT%f|LA9fCw>`enm$p7W^E@U7RNBtsh{_-7vVz3DtB*y#*~(L9+x9*wn8VjWw|Q~q zKFsj1Yl>;}%MG3=PY`$g$_mnyhuV&~O~u~)968$0b2!Jkd;2MtAP#ZDYw9hmK_+M$ zb3pxyYC&|CuAbtiG8HZjj?MZJBFbt`ryf+c1dXFuC z0*ZQhBzNBd*}s6K_G}(|Z_9NDV162#y%WSNe|FTDDhx)K!c(mMJh@h87@8(^YdK$&d*^WQe8Z53 z(|@MRJ$Lk-&ii74MPIs80WsOFZ(NX23oR-?As+*aq6b?~62@fSVmM-_*cb1RzZ)`5$agEiL`-E9s7{GM2?(KNPgK1(+c*|-FKoy}X(D_b#etO|YR z(BGZ)0Ntfv-7R4GHoXp?l5g#*={S1{u-QzxCGng*oWr~@X-5f~RA14b8~B+pLKvr4 zfgL|7I>jlak9>D4=(i(cqYf7#318!OSR=^`xxvI!bBlS??`xxWeg?+|>MxaIdH1U~#1tHu zB{QMR?EGRmQ_l4p6YXJ{o(hh-7Tdm>TAX380TZZZyVkqHNzjUn*_|cb?T? zt;d2s-?B#Mc>T-gvBmQZx(y_cfkXZO~{N zT6rP7SD6g~n9QJ)8F*8uHxTLCAZ{l1Y&?6v)BOJZ)=R-pY=Y=&1}jE7fQ>USS}xP#exo57uND0i*rEk@$;nLvRB@u~s^dwRf?G?_enN@$t* zbL%JO=rV(3Ju8#GqUpeE3l_Wu1lN9Y{D4uaUe`g>zlj$1ER$6S6@{m1!~V|bYkhZA z%CvrDRTkHuajMU8;&RZ&itnC~iYLW4DVkP<$}>#&(`UO>!n)Po;Mt(SY8Yb`AS9lt znbX^i?Oe9r_o=?})IHKHoQGKXsps_SE{hwrg?6dMI|^+$CeC&z@*LuF+P`7LfZ*yr+KN8B4{Nzv<`A(wyR@!|gw{zB6Ha ziwPAYh)oJ(nlqSknu(8g9N&1hu0$vFK$W#mp%>X~AU1ay+EKWcFdif{% z#4!4aoVVJ;ULmkQf!ke2}3hqxLK>eq|-d7Ly7-J9zMpT`?dxo6HdfJA|t)?qPEVBDv z{y_b?4^|YA4%WW0VZd8C(ZgQzRI5(I^)=Ub`Y#MHc@nv0w-DaJAqsbEHDWG8Ia6ju zo-iyr*sq((gEwCC&^TYBWt4_@|81?=B-?#P6NMff(*^re zYqvDuO`K@`mjm_Jd;mW_tP`3$cS?R$jR1ZN09$YO%_iBqh5ftzSpMQQtxKFU=FYmP zeY^jph+g<4>YO;U^O>-NFLn~-RqlHvnZl2yd2A{Yc1G@Ga$d+Q&(f^tnPf+Z7serIU};17+2DU_f4Z z@GaPFut27d?!YiD+QP@)T=77cR9~MK@bd~pY%X(h%L={{OIb8IQmf-!xmZkm8A0Ga zQSWONI17_ru5wpHg3jI@i9D+_Y|pCqVuHJNdHUauTD=R$JcD2K_liQisqG$(sm=k9;L* z!L?*4B~ql7uioSX$zWJ?;q-SWXRFhz2Jt4%fOHA=Bwf|RzhwqdXGr78y$J)LR7&3T zE1WWz*>GPWKZ0%|@%6=fyx)5rzUpI;bCj>3RKzNG_1w$fIFCZ&UR0(7S?g}`&Pg$M zf`SLsz8wK82Vyj7;RyKmY{a8G{2BHG%w!^T|Njr!h9TO2LaP^_f22Q1=l$QiU84ao zHe_#{S6;qrC6w~7{y(hs-?-j?lbOfgH^E=XcSgnwW*eEz{_Z<_xN#0001NP)t-s|Ns9~ z#rXRE|M&d=0au&!`~QyF`q}dRnBDt}*!qXo`c{v z{Djr|@Adh0(D_%#_&mM$D6{kE_x{oE{l@J5@%H*?%=t~i_`ufYOPkAEn!pfkr2$fs z652Tz0001XNklqeeKN4RM4i{jKqmiC$?+xN>3Apn^ z0QfuZLym_5b<*QdmkHjHlj811{If)dl(Z2K0A+ekGtrFJb?g|wt#k#pV-#A~bK=OT ts8>{%cPtyC${m|1#B1A6#u!Q;umknL1chzTM$P~L002ovPDHLkV1lTfnu!1a 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..f68554b --- /dev/null +++ b/ios/Runner/Info.plist @@ -0,0 +1,70 @@ + + + + + CADisableMinimumFrameDurationOnPhone + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Magic Example + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + magic_example + 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/_plugins.g.dart b/lib/app/_plugins.g.dart new file mode 100644 index 0000000..33a34ea --- /dev/null +++ b/lib/app/_plugins.g.dart @@ -0,0 +1,27 @@ +// GENERATED: do not edit by hand. +// Regenerate via: dart run magic:artisan plugins:refresh +// +// Source: .artisan/plugins.json + +import 'package:fluttersdk_artisan/artisan.dart'; +import 'package:fluttersdk_dusk/cli.dart' show FluttersdkDuskArtisanProvider; +import 'package:fluttersdk_telescope/cli.dart' + show FluttersdkTelescopeArtisanProvider; +import 'package:magic/cli.dart' show MagicArtisanProvider; +import 'package:magic_deeplink/cli.dart' show MagicDeeplinkArtisanProvider; +import 'package:magic_notifications/cli.dart' + show MagicNotificationsArtisanProvider; +import 'package:magic_social_auth/cli.dart' show MagicSocialAuthArtisanProvider; +import 'package:magic_starter/cli.dart' show MagicStarterArtisanProvider; + +List autoDiscoveredProviders() { + return [ + FluttersdkDuskArtisanProvider(), + FluttersdkTelescopeArtisanProvider(), + MagicArtisanProvider(), + MagicDeeplinkArtisanProvider(), + MagicNotificationsArtisanProvider(), + MagicSocialAuthArtisanProvider(), + MagicStarterArtisanProvider(), + ]; +} diff --git a/lib/app/commands/_index.g.dart b/lib/app/commands/_index.g.dart new file mode 100644 index 0000000..8da1c7c --- /dev/null +++ b/lib/app/commands/_index.g.dart @@ -0,0 +1,7 @@ +// AUTO-GENERATED — DO NOT EDIT. +// Regenerate via: artisan commands:refresh +// (Also auto-updated by: artisan make:command ) + +import 'package:fluttersdk_artisan/artisan.dart'; + +List get commands => []; diff --git a/lib/app/kernel.dart b/lib/app/kernel.dart new file mode 100644 index 0000000..fe14464 --- /dev/null +++ b/lib/app/kernel.dart @@ -0,0 +1,55 @@ +// Import Magic to access Kernel, middleware base classes, etc.: +import 'package:magic/magic.dart'; +import 'middleware/ensure_authenticated.dart'; +import 'middleware/redirect_if_authenticated.dart'; + +/// The HTTP Kernel. +/// +/// Register all middleware here, similar to Laravel's `app/Http/Kernel.php`. +/// +/// ## Usage +/// +/// This function is called automatically by `RouteServiceProvider.register()`. +/// You do not need to call it manually. +/// +/// ## Global Middleware +/// +/// Global middleware runs on EVERY route: +/// +/// ```dart +/// Kernel.global([ +/// () => LoggingMiddleware(), +/// ]); +/// ``` +/// +/// ## Route Middleware +/// +/// Route middleware are named aliases you use in route definitions: +/// +/// ```dart +/// Kernel.registerAll({ +/// 'auth': () => EnsureAuthenticated(), +/// 'guest': () => RedirectIfAuthenticated(), +/// }); +/// ``` +void registerKernel() { + // --------------------------------------------------------------------------- + // Global Middleware + // --------------------------------------------------------------------------- + // Kernel.global([ + // () => LoggingMiddleware(), + // ]); + + // --------------------------------------------------------------------------- + // Route Middleware + // --------------------------------------------------------------------------- + // Uncomment and add your middleware aliases below: + // Kernel.registerAll({ + // 'auth': () => EnsureAuthenticated(), + // 'guest': () => RedirectIfAuthenticated(), + // }); + Kernel.registerAll({ + 'auth': () => EnsureAuthenticated(), + 'guest': () => RedirectIfAuthenticated(), + }); +} diff --git a/lib/app/middleware/ensure_authenticated.dart b/lib/app/middleware/ensure_authenticated.dart new file mode 100644 index 0000000..cce3e9c --- /dev/null +++ b/lib/app/middleware/ensure_authenticated.dart @@ -0,0 +1,23 @@ +import 'package:magic/magic.dart'; +import 'package:magic_starter/magic_starter.dart'; + +/// Middleware that redirects unauthenticated users to the login page. +/// +/// Use this in auth-only route groups: +/// +/// ```dart +/// MagicRoute.group( +/// middleware: [EnsureAuthenticated()], +/// routes: () { /* protected routes */ }, +/// ); +/// ``` +class EnsureAuthenticated extends MagicMiddleware { + @override + Future handle(void Function() next) async { + if (!Auth.check()) { + MagicRoute.to(MagicStarterConfig.loginRoute()); + return; + } + next(); + } +} diff --git a/lib/app/middleware/redirect_if_authenticated.dart b/lib/app/middleware/redirect_if_authenticated.dart new file mode 100644 index 0000000..29992ae --- /dev/null +++ b/lib/app/middleware/redirect_if_authenticated.dart @@ -0,0 +1,23 @@ +import 'package:magic/magic.dart'; +import 'package:magic_starter/magic_starter.dart'; + +/// Middleware that redirects authenticated users away from guest-only pages. +/// +/// Use this in guest-only route groups (auth pages): +/// +/// ```dart +/// MagicRoute.group( +/// middleware: [RedirectIfAuthenticated()], +/// routes: () { /* auth pages */ }, +/// ); +/// ``` +class RedirectIfAuthenticated extends MagicMiddleware { + @override + Future handle(void Function() next) async { + if (Auth.check()) { + MagicRoute.to(MagicStarterConfig.homeRoute()); + return; + } + next(); + } +} diff --git a/lib/app/models/team.dart b/lib/app/models/team.dart new file mode 100644 index 0000000..ca6fa2d --- /dev/null +++ b/lib/app/models/team.dart @@ -0,0 +1,177 @@ +import 'dart:convert'; + +import 'package:magic/magic.dart'; +import 'package:magic_starter/magic_starter.dart'; + +/// Team model. +/// +/// Represents a team/workspace in the application. Extends the Magic +/// ORM [Model] with [HasTimestamps] and [InteractsWithPersistence] mixins +/// to provide full persistence and timestamp tracking. +/// +/// ## Usage +/// +/// ```dart +/// // Create a new team +/// final team = Team()..name = 'My Team'; +/// await team.save(); +/// +/// // Find a team by ID +/// final team = await Team.find('abc123'); +/// Log.debug(team?.name); +/// ``` +/// +/// ## Plugin Interop +/// +/// ```dart +/// // Convert to MagicStarterTeam for the magic_starter team resolver +/// final magicStarterTeam = team.toMagicStarterTeam(); +/// ``` +class Team extends Model with HasTimestamps, InteractsWithPersistence { + /// The table associated with the model. + @override + String get table => 'teams'; + + /// The API resource for remote operations. + @override + String get resource => 'teams'; + + /// Whether the primary key is auto-incrementing. + /// + /// Set to false because this app uses string UUIDs as primary keys. + @override + bool get incrementing => false; + + /// The attributes that are mass assignable. + @override + List get fillable => ['name']; + + /// The attributes that should be cast. + @override + Map get casts => {}; + + // --------------------------------------------------------------------------- + // Typed Accessors + // --------------------------------------------------------------------------- + + /// Get the team's ID. + @override + String get id => getAttribute('id')?.toString() ?? ''; + + /// Get the team name. + String? get name => getAttribute('name') as String?; + + /// Set the team name. + set name(String? value) => setAttribute('name', value); + + /// Get the URL of the team's profile photo. + String? get profilePhotoUrl => getAttribute('profile_photo_url') as String?; + + /// Set the URL of the team's profile photo. + set profilePhotoUrl(String? value) => + setAttribute('profile_photo_url', value); + + /// Check if this is a personal team (automatically created for the owner). + bool get isPersonalTeam => getAttribute('personal_team') == true; + + /// Get the ID of the team's owner. + String? get ownerId => getAttribute('owner_id')?.toString(); + + /// Current authenticated user's role in this team (from API response). + /// + /// Typical values: `'owner'`, `'admin'`, `'editor'`, `'member'`. + String? get userRole => getAttribute('user_role') as String?; + + // --------------------------------------------------------------------------- + // Permission Helpers + // --------------------------------------------------------------------------- + + /// Whether the current user can manage team members (owner or admin). + bool get canManageMembers => userRole == 'owner' || userRole == 'admin'; + + /// Whether the current user can edit team settings. + /// + /// Restricted to the owner only. Use [canManageMembers] for broader + /// write access that includes admins. + bool get canEdit => isOwner; + + /// Whether the current authenticated user is the owner of this team. + bool get isOwner => Auth.id() == ownerId; + + // --------------------------------------------------------------------------- + // Static Helpers + // --------------------------------------------------------------------------- + + /// Find a team by ID. + /// + /// Returns `null` if no team with the given [id] exists. + /// + /// ```dart + /// final team = await Team.find('abc123'); + /// ``` + static Future find(dynamic id) => + InteractsWithPersistence.findById(id, Team.new); + + /// Get all teams. + /// + /// ```dart + /// final teams = await Team.all(); + /// ``` + static Future> all() => + InteractsWithPersistence.allModels(Team.new); + + // --------------------------------------------------------------------------- + // Factory Methods + // --------------------------------------------------------------------------- + + /// Create a [Team] from a [Map]. + /// + /// Uses [setRawAttributes] to hydrate the model directly from raw API data, + /// bypassing mass-assignment protection. The [exists] flag is set based on + /// whether the map contains an `id` key. + /// + /// ```dart + /// final team = Team.fromMap({'id': 'abc123', 'name': 'Acme Corp'}); + /// ``` + static Team fromMap(Map map) { + return Team() + ..setRawAttributes(map, sync: true) + ..exists = map.containsKey('id'); + } + + /// Create a [Team] from a JSON string. + /// + /// Decodes [json] and delegates to [fromMap]. + /// + /// ```dart + /// final team = Team.fromJson('{"id":"abc123","name":"Acme Corp"}'); + /// ``` + static Team fromJson(String json) { + final map = jsonDecode(json) as Map; + return Team.fromMap(map); + } + + // --------------------------------------------------------------------------- + // Plugin Interop + // --------------------------------------------------------------------------- + + /// Convert to [MagicStarterTeam] for the magic_starter plugin's team resolver. + /// + /// Used by [MagicStarter.useTeamResolver] so the plugin can access + /// team data without depending on the host app's concrete [Team] class. + /// + /// ```dart + /// MagicStarter.useTeamResolver( + /// currentTeam: () async => (await Team.find(currentId))?.toMagicStarterTeam(), + /// allTeams: () async => (await Team.all()).map((t) => t.toMagicStarterTeam()).toList(), + /// ); + /// ``` + MagicStarterTeam toMagicStarterTeam() { + return MagicStarterTeam( + id: id, + name: name ?? '', + photoUrl: profilePhotoUrl, + isPersonalTeam: isPersonalTeam, + ); + } +} diff --git a/lib/app/models/user.dart b/lib/app/models/user.dart new file mode 100644 index 0000000..33abecc --- /dev/null +++ b/lib/app/models/user.dart @@ -0,0 +1,189 @@ +import 'dart:convert'; + +import 'package:magic/magic.dart'; + +import 'team.dart'; + +/// User model. +/// +/// Represents the authenticated user in the application. Extends the Magic +/// ORM [Model] with [HasTimestamps], [InteractsWithPersistence], and +/// [Authenticatable] mixins to provide full persistence, timestamp tracking, +/// and authentication support. +/// +/// ## Usage with Typed Accessors +/// +/// ```dart +/// final user = await User.find('abc123'); +/// Log.debug(user?.name); // Uses typed accessor +/// user?.name = 'Updated Name'; +/// await user?.save(); +/// ``` +/// +/// ## Usage with Convenient get/set +/// +/// ```dart +/// final user = await User.find('abc123'); +/// Log.debug(user?.get('name', defaultValue: 'Unknown')); +/// user?.set('name', 'Updated Name'); +/// await user?.save(); +/// ``` +/// +/// ## Accessing the Authenticated User +/// +/// ```dart +/// final user = User.current; +/// Log.debug(user.name); +/// ``` +class User extends Model + with HasTimestamps, InteractsWithPersistence, Authenticatable { + /// The table associated with the model. + @override + String get table => 'users'; + + /// The API resource for remote operations. + @override + String get resource => 'users'; + + /// Whether the primary key is auto-incrementing. + /// + /// Set to false because this app uses string UUIDs as primary keys. + @override + bool get incrementing => false; + + /// The attributes that are mass assignable. + @override + List get fillable => [ + 'name', + 'email', + 'phone', + 'timezone', + 'language', + ]; + + /// The attributes that should be cast. + @override + Map get casts => {}; + + // --------------------------------------------------------------------------- + // Typed Accessors + // --------------------------------------------------------------------------- + + /// Get the user's ID. + @override + String get id => getAttribute('id')?.toString() ?? ''; + + /// Get the user's name. + String? get name => getAttribute('name') as String?; + + /// Set the user's name. + set name(String? value) => setAttribute('name', value); + + /// Get the user's email address. + String? get email => getAttribute('email') as String?; + + /// Set the user's email address. + set email(String? value) => setAttribute('email', value); + + /// Get the user's phone number. + String? get phone => getAttribute('phone') as String?; + + /// Set the user's phone number. + set phone(String? value) => setAttribute('phone', value); + + /// Get the user's timezone identifier (e.g. `America/New_York`). + String? get timezone => getAttribute('timezone') as String?; + + /// Set the user's timezone identifier. + set timezone(String? value) => setAttribute('timezone', value); + + /// Get the user's preferred language code (e.g. `en`, `tr`). + String? get language => getAttribute('language') as String?; + + /// Set the user's preferred language code. + set language(String? value) => setAttribute('language', value); + + /// Get the URL of the user's profile photo. + String? get profilePhotoUrl => getAttribute('profile_photo_url') as String?; + + /// The user's current team. + Team? get currentTeam { + final Map? data = + getAttribute('current_team') as Map?; + return data != null ? Team.fromMap(data) : null; + } + + /// All teams the user belongs to. + List get allTeams { + final List data = + getAttribute('all_teams') as List? ?? []; + return data.map((t) => Team.fromMap(t as Map)).toList(); + } + + // --------------------------------------------------------------------------- + // Static Helpers + // --------------------------------------------------------------------------- + + /// Find a user by ID. + /// + /// Returns `null` if no user with the given [id] exists. + /// + /// ```dart + /// final user = await User.find('abc123'); + /// ``` + static Future find(dynamic id) => + InteractsWithPersistence.findById(id, User.new); + + /// Get all users. + /// + /// ```dart + /// final users = await User.all(); + /// ``` + static Future> all() => + InteractsWithPersistence.allModels(User.new); + + /// Get the currently authenticated user. + /// + /// Returns an empty [User] instance when no user is authenticated. Callers + /// should prefer `Auth.check()` before accessing user data to avoid + /// operating on an empty model. + /// + /// ```dart + /// if (Auth.check()) { + /// final user = User.current; + /// Log.debug(user.name); + /// } + /// ``` + static User get current => Auth.user() ?? User(); + + // --------------------------------------------------------------------------- + // Flutter-Familiar Factory Methods + // --------------------------------------------------------------------------- + + /// Create a [User] from a [Map]. + /// + /// Uses [setRawAttributes] to hydrate the model directly from raw API data, + /// bypassing mass-assignment protection. The [exists] flag is set based on + /// whether the map contains an `id` key. + /// + /// ```dart + /// final user = User.fromMap({'id': 'abc123', 'name': 'Jane', 'email': 'jane@example.com'}); + /// ``` + static User fromMap(Map map) { + return User() + ..setRawAttributes(map, sync: true) + ..exists = map.containsKey('id'); + } + + /// Create a [User] from a JSON string. + /// + /// Decodes [json] and delegates to [fromMap]. + /// + /// ```dart + /// final user = User.fromJson('{"id":"abc123","name":"Jane"}'); + /// ``` + static User fromJson(String json) { + final map = jsonDecode(json) as Map; + return User.fromMap(map); + } +} diff --git a/lib/app/providers/app_service_provider.dart b/lib/app/providers/app_service_provider.dart new file mode 100644 index 0000000..3d2e6fc --- /dev/null +++ b/lib/app/providers/app_service_provider.dart @@ -0,0 +1,76 @@ +import 'package:magic/magic.dart'; +import 'package:flutter/material.dart'; +import 'package:magic_starter/magic_starter.dart'; +import '../models/user.dart'; + +/// Application Service Provider. +/// +/// Use this provider to bind your own services to the IoC container and +/// to perform any bootstrap logic that requires other services to be ready. +class AppServiceProvider extends ServiceProvider { + AppServiceProvider(super.app); + + @override + void register() { + // Bind your services here (sync only — do not resolve other services). + // Example: + // app.singleton('my_service', () => MyService()); + } + + @override + Future boot() async { + // Perform async bootstrap logic here. + // + // IMPORTANT: Call setUserFactory() so Auth.user() returns your model: + // Auth.manager.setUserFactory((data) => User.fromMap(data)); + // Magic Starter: Register user factory for auth session restoration. + Auth.manager.setUserFactory((data) => User.fromMap(data)); + MagicStarter.useUserModel((data) => User.fromMap(data)); + + // Magic Starter: Navigation items for sidebar and mobile bottom bar. + MagicStarter.useNavigation( + mainItems: [ + MagicStarterNavItem( + icon: Icons.dashboard_outlined, + labelKey: 'nav.dashboard', + path: MagicStarterConfig.homeRoute(), + ), + MagicStarterNavItem( + icon: Icons.settings_outlined, + labelKey: 'nav.settings', + path: MagicStarterConfig.profileRoute(), + ), + ], + bottomItems: [ + MagicStarterNavItem( + icon: Icons.dashboard_outlined, + labelKey: 'nav.dashboard', + path: MagicStarterConfig.homeRoute(), + ), + MagicStarterNavItem( + icon: Icons.settings_outlined, + labelKey: 'nav.settings', + path: MagicStarterConfig.profileRoute(), + ), + ], + ); + + // Magic Starter: Logout callback. + MagicStarter.useLogout(() async { + await Auth.logout(); + MagicRoute.to(MagicStarterConfig.loginRoute()); + }); + + // Magic Starter: Supported locale options for profile settings. + MagicStarter.useLocaleOptions({'en': 'English'}); + + // Magic Starter: Team resolver for sidebar team switcher. + MagicStarter.useTeamResolver( + currentTeam: () => User.current.currentTeam?.toMagicStarterTeam(), + allTeams: () => + User.current.allTeams.map((t) => t.toMagicStarterTeam()).toList(), + onSwitch: (teamId) => + MagicStarterTeamController.instance.switchTeam(teamId), + ); + } +} diff --git a/lib/app/providers/route_service_provider.dart b/lib/app/providers/route_service_provider.dart new file mode 100644 index 0000000..983a0d1 --- /dev/null +++ b/lib/app/providers/route_service_provider.dart @@ -0,0 +1,40 @@ +import 'package:flutter/foundation.dart' show kDebugMode; +import 'package:magic/magic.dart'; +import 'package:magic_devtools/preview.dart'; + +import '../kernel.dart'; +import '../../routes/app.dart'; +import '../../preview/_previews.g.dart'; +import 'package:magic_starter/magic_starter.dart'; + +/// Route Service Provider. +/// +/// Registers the HTTP kernel and application routes. +class RouteServiceProvider extends ServiceProvider { + RouteServiceProvider(super.app); + + @override + void register() { + // Register middleware kernel — runs synchronously during bootstrap. + registerKernel(); + } + + @override + Future boot() async { + // Register application route definitions. + registerMagicStarterAuthRoutes(); + registerMagicStarterProfileRoutes(); + registerMagicStarterTeamRoutes(); + registerAppRoutes(); + + // Dev-only component preview catalog. Registered here, in boot(), so it + // lands BEFORE MagicRouter first builds its routerConfig (the router locks + // its route table on first access). The kDebugMode guard lets the optimizer + // prove the whole catalog dead in release; registerRoutes() folds itself + // out behind kReleaseMode + PREVIEW_ENABLED as a second line of defence. + if (kDebugMode) { + MagicPreview.register(previewEntries()); + MagicPreview.registerRoutes(); + } + } +} diff --git a/lib/config/app.dart b/lib/config/app.dart new file mode 100644 index 0000000..1d8d9ca --- /dev/null +++ b/lib/config/app.dart @@ -0,0 +1,33 @@ +import 'package:magic/magic.dart'; +import '../app/providers/app_service_provider.dart'; +import '../app/providers/route_service_provider.dart'; +import 'package:magic_deeplink/magic_deeplink.dart'; +import 'package:magic_notifications/magic_notifications.dart'; +import 'package:magic_social_auth/magic_social_auth.dart'; +import 'package:magic_starter/magic_starter.dart'; + +/// Application Configuration. +Map get appConfig => { + 'app': { + 'name': env('APP_NAME', 'My App'), + 'env': env('APP_ENV', 'production'), + 'debug': env('APP_DEBUG', false), + 'key': env('APP_KEY'), + 'providers': [ + (app) => RouteServiceProvider(app), + (app) => CacheServiceProvider(app), + (app) => DatabaseServiceProvider(app), + (app) => LaunchServiceProvider(app), + (app) => LocalizationServiceProvider(app), + (app) => NetworkServiceProvider(app), + (app) => VaultServiceProvider(app), + (app) => BroadcastServiceProvider(app), + (app) => AppServiceProvider(app), + (app) => AuthServiceProvider(app), + (app) => DeeplinkServiceProvider(app), + (app) => NotificationServiceProvider(app), + (app) => SocialAuthServiceProvider(app), + (app) => MagicStarterServiceProvider(app), + ], + }, +}; diff --git a/lib/config/auth.dart b/lib/config/auth.dart new file mode 100644 index 0000000..80e6765 --- /dev/null +++ b/lib/config/auth.dart @@ -0,0 +1,56 @@ +/// Authentication Configuration. +/// +/// ## Guards +/// +/// - `bearer` / `sanctum` — Bearer token (default) +/// - `basic` — HTTP Basic auth +/// - `api_key` — API key auth +/// +/// ## Features +/// +/// - User caching (instant restore) +/// - Auto token refresh on 401 +/// - Driver-agnostic interceptors +final Map authConfig = { + 'auth': { + // ------------------------------------------------------------------------- + // Defaults + // ------------------------------------------------------------------------- + 'defaults': {'guard': 'api'}, + + // ------------------------------------------------------------------------- + // Guards + // ------------------------------------------------------------------------- + 'guards': { + 'api': {'driver': 'bearer'}, + }, + + // ------------------------------------------------------------------------- + // Endpoints + // ------------------------------------------------------------------------- + 'endpoints': { + 'user': '/auth/user', // Fetch user on restore + 'refresh': '/auth/refresh', // Refresh access token + }, + + // ------------------------------------------------------------------------- + // Token + // ------------------------------------------------------------------------- + 'token': { + 'key': 'auth_token', + 'refresh_key': 'refresh_token', + 'header': 'Authorization', + 'prefix': 'Bearer', + }, + + // ------------------------------------------------------------------------- + // Cache + // ------------------------------------------------------------------------- + 'cache': {'user_key': 'auth_user'}, + + // ------------------------------------------------------------------------- + // Auto Restore + // ------------------------------------------------------------------------- + 'auto_refresh': true, + }, +}; diff --git a/lib/config/broadcasting.dart b/lib/config/broadcasting.dart new file mode 100644 index 0000000..776c275 --- /dev/null +++ b/lib/config/broadcasting.dart @@ -0,0 +1,26 @@ +import 'package:magic/magic.dart'; + +/// Broadcasting configuration. +/// +/// Defines the default broadcasting connection and available connections. +/// See: https://magic.fluttersdk.com/docs/broadcasting +Map get broadcastingConfig => { + 'broadcasting': { + 'default': env('BROADCAST_CONNECTION', 'null'), + 'connections': { + 'reverb': { + 'driver': 'reverb', + 'host': env('REVERB_HOST', 'localhost'), + 'port': int.tryParse(env('REVERB_PORT', '8080')) ?? 8080, + 'scheme': env('REVERB_SCHEME', 'ws'), + 'app_key': env('REVERB_APP_KEY', ''), + 'auth_endpoint': '/broadcasting/auth', + 'reconnect': true, + 'max_reconnect_delay': 30000, + 'activity_timeout': 120, + 'dedup_buffer_size': 100, + }, + 'null': {'driver': 'null'}, + }, + }, +}; diff --git a/lib/config/cache.dart b/lib/config/cache.dart new file mode 100644 index 0000000..0ad53fa --- /dev/null +++ b/lib/config/cache.dart @@ -0,0 +1,9 @@ +import 'package:magic/magic.dart'; + +/// Cache Configuration. +/// +/// - `driver`: `FileStore()` for persistent disk caching. +/// - `ttl`: default time-to-live in seconds. +Map get cacheConfig => { + 'cache': {'driver': FileStore(), 'ttl': 3600}, +}; diff --git a/lib/config/database.dart b/lib/config/database.dart new file mode 100644 index 0000000..d11f1a0 --- /dev/null +++ b/lib/config/database.dart @@ -0,0 +1,12 @@ +/// Database Configuration. +/// +/// Uses SQLite by default. On mobile, files are stored in the app's documents +/// directory. On web, in-memory SQLite is used automatically. +Map get databaseConfig => { + 'database': { + 'default': 'sqlite', + 'connections': { + 'sqlite': {'driver': 'sqlite', 'database': 'database.sqlite'}, + }, + }, +}; diff --git a/lib/config/deeplink.dart b/lib/config/deeplink.dart new file mode 100644 index 0000000..4814721 --- /dev/null +++ b/lib/config/deeplink.dart @@ -0,0 +1,17 @@ +Map get deeplinkConfig => { + 'deeplink': { + 'enabled': true, + 'driver': 'app_links', + 'domain': 'example.com', + 'scheme': 'https', + + 'ios': {'team_id': 'YOUR_TEAM_ID', 'bundle_id': 'com.example.app'}, + + 'android': { + 'package_name': 'com.example.app', + 'sha256_fingerprints': ['YOUR_SHA256_FINGERPRINT'], + }, + + 'paths': ['/*'], + }, +}; diff --git a/lib/config/logging.dart b/lib/config/logging.dart new file mode 100644 index 0000000..28dcf03 --- /dev/null +++ b/lib/config/logging.dart @@ -0,0 +1,16 @@ +/// Logging Configuration. +/// +/// This config file is OPTIONAL. Only create it if you want to customize +/// the logging behaviour. The default channel is `stack` which logs to console. +Map get loggingConfig => { + 'logging': { + 'default': 'stack', + 'channels': { + 'stack': { + 'driver': 'stack', + 'channels': ['console'], + }, + 'console': {'driver': 'console', 'level': 'debug'}, + }, + }, +}; diff --git a/lib/config/magic_starter.dart b/lib/config/magic_starter.dart new file mode 100644 index 0000000..9bad64f --- /dev/null +++ b/lib/config/magic_starter.dart @@ -0,0 +1,36 @@ +// Magic Starter Configuration. +// +// This file is auto-generated by the magic_starter install command. +// Customize feature toggles and route prefixes as needed. + +Map get magicStarterConfig => { + 'magic_starter': { + 'features': { + 'teams': true, + 'registration': true, + 'extended_profile': false, + 'profile_photos': false, + 'social_login': false, + 'two_factor': true, + 'sessions': true, + 'phone_otp': false, + 'newsletter': false, + 'notifications': false, + 'email_verification': true, + 'guest_auth': false, + 'timezones': false, + }, + 'auth': {'email': true, 'phone': false}, + 'defaults': {'locale': 'en', 'timezone': 'UTC'}, + 'supported_locales': ['en', 'tr'], + 'routes': { + 'home': '/', + 'login': '/auth/login', + 'auth_prefix': '/auth', + 'teams_prefix': '/teams', + 'profile_prefix': '/settings', + 'notifications_prefix': '/notifications', + }, + 'legal': {'terms_url': null, 'privacy_url': null}, + }, +}; diff --git a/lib/config/network.dart b/lib/config/network.dart new file mode 100644 index 0000000..0d1db09 --- /dev/null +++ b/lib/config/network.dart @@ -0,0 +1,22 @@ +import 'package:magic/magic.dart'; + +/// Network Configuration. +/// +/// This config file is OPTIONAL. Only create it if you want to use the +/// Magic Network (Http) system. Don't forget to add `NetworkServiceProvider` +/// to your `app.providers` list. +Map get networkConfig => { + 'network': { + 'default': 'api', + 'drivers': { + 'api': { + 'base_url': env('API_URL', 'http://localhost:8000/api/v1'), + 'timeout': 10000, + 'headers': { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + }, + }, + }, + }, +}; diff --git a/lib/config/routing.dart b/lib/config/routing.dart new file mode 100644 index 0000000..e19d3e1 --- /dev/null +++ b/lib/config/routing.dart @@ -0,0 +1,11 @@ +/// Routing Configuration. +/// +/// Controls URL strategy and other routing behavior. +/// Set `'url_strategy'` to `'path'` for clean web URLs (/dashboard instead of /#/dashboard). +/// See: https://magic.fluttersdk.com/docs/basics/routing#url-strategy +Map get routingConfig => { + 'routing': { + 'url_strategy': + null, // 'path' for clean URLs on web, null for default hash strategy + }, +}; diff --git a/lib/config/view.dart b/lib/config/view.dart new file mode 100644 index 0000000..fa8abdd --- /dev/null +++ b/lib/config/view.dart @@ -0,0 +1,21 @@ +/// View Configuration. +/// +/// Customizes the appearance of Magic UI components (dialogs, confirms, +/// loading). These className values are read by MagicFeedback via +/// `Config.get('view.*')`. +Map get viewConfig => { + 'view': { + 'dialog': { + 'class': 'bg-white dark:bg-gray-800 rounded-xl p-6 shadow-2xl max-w-lg', + }, + 'confirm': { + 'container_class': + 'bg-white dark:bg-gray-800 rounded-xl p-6 shadow-2xl w-80', + 'title_class': 'text-lg font-bold text-gray-900 dark:text-white', + 'message_class': 'text-gray-600 dark:text-gray-400 mt-2', + 'button_cancel_class': 'px-4 py-2 text-gray-600 dark:text-gray-300', + 'button_confirm_class': 'px-4 py-2 bg-primary text-white rounded-lg', + 'button_danger_class': 'px-4 py-2 bg-red-500 text-white rounded-lg', + }, + }, +}; diff --git a/lib/config/wind_theme.g.dart b/lib/config/wind_theme.g.dart new file mode 100644 index 0000000..39a5e73 --- /dev/null +++ b/lib/config/wind_theme.g.dart @@ -0,0 +1,52 @@ +// GENERATED: do not edit by hand. +// Regenerate via: dart run magic:artisan design:sync +// +// Source of truth: DESIGN.md + +import 'package:flutter/material.dart'; + +/// Semantic wind alias map generated from DESIGN.md. +/// +/// Drop-in for `WindThemeData(aliases: designAliases)`; the +/// keys match the magic_starter token contract. +const Map designAliases = { + 'bg-surface': 'bg-[#FFFFFF] dark:bg-[#030712]', + 'bg-surface-container': 'bg-[#F9FAFB] dark:bg-[#111827]', + 'bg-surface-container-high': 'bg-[#F3F4F6] dark:bg-[#1F2937]', + 'text-fg': 'text-[#111827] dark:text-[#F9FAFB]', + 'text-fg-muted': 'text-[#6B7280] dark:text-[#9CA3AF]', + 'text-fg-disabled': 'text-[#D1D5DB] dark:text-[#4B5563]', + 'bg-primary': 'bg-[#7C3AED] dark:bg-[#8B5CF6]', + 'text-on-primary': 'text-[#FFFFFF] dark:text-[#FFFFFF]', + 'bg-primary-container': 'bg-[#EDE9FE] dark:bg-[#4C1D95]', + 'bg-accent': 'bg-[#4F46E5] dark:bg-[#6366F1]', + 'border-color-border': 'border-[#E5E7EB] dark:border-[#374151]', + 'border-color-border-subtle': 'border-[#F3F4F6] dark:border-[#1F2937]', + 'bg-destructive': 'bg-[#DC2626] dark:bg-[#EF4444]', + 'text-on-destructive': 'text-[#FFFFFF] dark:text-[#FFFFFF]', + 'bg-destructive-container': 'bg-[#FEE2E2] dark:bg-[#7F1D1D]', + 'bg-success': 'bg-[#15803D] dark:bg-[#16A34A]', + 'bg-warning': 'bg-[#D97706] dark:bg-[#B45309]', +}; + +/// The brand `primary` color with a generated 50-900 ramp. +/// +/// Seeded from the DESIGN.md `primary` light hex; consumed by +/// `WindThemeData.toThemeData()` Material interop. +final Map designColors = + { + 'primary': MaterialColor( + 0xFF7C3AED, + { + 50: Color(0xFFF5EFFE), + 100: Color(0xFFEADFFC), + 200: Color(0xFFD2BCF9), + 300: Color(0xFFBB99F6), + 400: Color(0xFF9E6DF2), + 500: Color(0xFF7C3AED), + 600: Color(0xFF6D33D1), + 700: Color(0xFF5E2CB4), + 800: Color(0xFF4F2598), + 900: Color(0xFF401E7B), + }), +}; diff --git a/lib/main.dart b/lib/main.dart new file mode 100644 index 0000000..61107c8 --- /dev/null +++ b/lib/main.dart @@ -0,0 +1,61 @@ +import 'package:flutter/material.dart'; +import 'package:magic/magic.dart'; +import 'config/app.dart'; +import 'config/routing.dart'; +import 'config/view.dart'; +import 'config/auth.dart'; +import 'config/database.dart'; +import 'config/network.dart'; +import 'config/cache.dart'; +import 'config/logging.dart'; +import 'config/broadcasting.dart'; +import 'config/deeplink.dart'; +import 'config/wind_theme.g.dart'; +import 'package:flutter/foundation.dart' show kDebugMode; +import 'package:fluttersdk_dusk/dusk.dart'; +import 'package:magic_devtools/dusk.dart'; +import 'package:fluttersdk_telescope/telescope.dart'; +import 'package:magic_devtools/telescope.dart'; +import 'config/magic_starter.dart'; + +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + + if (kDebugMode) { + DuskPlugin.install(); + } + if (kDebugMode) { + TelescopePlugin.install(); + TelescopePlugin.registerWatcher(ExceptionWatcher()); + TelescopePlugin.registerWatcher(DumpWatcher()); + } + await Magic.init( + configFactories: [ + () => appConfig, + () => routingConfig, + () => viewConfig, + () => authConfig, + () => databaseConfig, + () => networkConfig, + () => cacheConfig, + () => loggingConfig, + () => broadcastingConfig, + () => deeplinkConfig, + () => magicStarterConfig, + ], + ); + if (kDebugMode) { + MagicTelescopeIntegration.install(); + } + if (kDebugMode) { + MagicDuskIntegration.install(); + } + // Theme generated from DESIGN.md via `design:sync`. Regenerate with: + // dart run magic_example:artisan design:sync + final windTheme = WindThemeData( + colors: designColors, + aliases: designAliases, + ); + + runApp(MagicApplication(title: 'Magic Example', windTheme: windTheme)); +} diff --git a/lib/preview/_previews.g.dart b/lib/preview/_previews.g.dart new file mode 100644 index 0000000..fda03c5 --- /dev/null +++ b/lib/preview/_previews.g.dart @@ -0,0 +1,59 @@ +// GENERATED: do not edit by hand. +// Regenerate via: dart run magic:artisan previews:refresh +// +// Source: *.preview.dart files discovered under the scan dir. + +import 'package:magic_devtools/preview.dart'; +import 'components.preview.dart'; +import 'dashboard_screen.preview.dart'; +import 'foundations.preview.dart'; +import 'login_screen.preview.dart'; +import 'profile_screen.preview.dart'; +import 'register_screen.preview.dart'; +import 'settings_screen.preview.dart'; +import 'teams_screen.preview.dart'; + +List previewEntries() { + return [ + PreviewEntry( + label: 'Components', + slug: 'components', + builder: (_) => const ComponentsPreview(), + ), + PreviewEntry( + label: 'DashboardScreen', + slug: 'dashboard_screen', + builder: (_) => const DashboardScreenPreview(), + ), + PreviewEntry( + label: 'Foundations', + slug: 'foundations', + builder: (_) => const FoundationsPreview(), + ), + PreviewEntry( + label: 'LoginScreen', + slug: 'login_screen', + builder: (_) => const LoginScreenPreview(), + ), + PreviewEntry( + label: 'ProfileScreen', + slug: 'profile_screen', + builder: (_) => const ProfileScreenPreview(), + ), + PreviewEntry( + label: 'RegisterScreen', + slug: 'register_screen', + builder: (_) => const RegisterScreenPreview(), + ), + PreviewEntry( + label: 'SettingsScreen', + slug: 'settings_screen', + builder: (_) => const SettingsScreenPreview(), + ), + PreviewEntry( + label: 'TeamsScreen', + slug: 'teams_screen', + builder: (_) => const TeamsScreenPreview(), + ), + ]; +} diff --git a/lib/preview/components.preview.dart b/lib/preview/components.preview.dart new file mode 100644 index 0000000..e8c3bbb --- /dev/null +++ b/lib/preview/components.preview.dart @@ -0,0 +1,190 @@ +import 'package:flutter/widgets.dart'; +import 'package:magic/magic.dart' show SelectOption, WDiv, WText; +import 'package:magic_starter/magic_starter.dart' + show + Badge, + BadgeTone, + Button, + ButtonIntent, + ButtonSize, + Card, + CardVariant, + Input, + InputState, + MagicFormField, + Select, + Switch, + Tabs, + Typography, + TypographyVariant; + +/// Components preview: a curated matrix of the key library components rendered +/// in their representative variants. +/// +/// This is a static variant matrix (the catalog renders it in light and dark); +/// interactive components are shown in fixed display states, matching the v1 +/// "no knobs" decision. Selection callbacks are no-ops so the snapshot stays +/// stable across rebuilds. +class ComponentsPreview extends StatelessWidget { + /// Creates the components preview. + const ComponentsPreview({super.key}); + + @override + Widget build(BuildContext context) { + return WDiv( + className: 'flex flex-col gap-8', + children: [ + _section('Buttons', _buildButtons()), + _section('Badges', _buildBadges()), + _section('Inputs', _buildInputs()), + _section('Switches', _buildSwitches()), + _section('Select', _buildSelect()), + _section('Tabs', _buildTabs()), + _section('Cards', _buildCards()), + ], + ); + } + + /// A labelled section wrapper. + Widget _section(String title, Widget body) { + return WDiv( + className: 'flex flex-col gap-3', + children: [ + WText(title, className: 'text-fg text-sm font-semibold uppercase'), + body, + ], + ); + } + + /// Every button intent at each size, plus the loading and disabled states. + Widget _buildButtons() { + return WDiv( + className: 'flex flex-col gap-3', + children: [ + for (final ButtonSize size in ButtonSize.values) + WDiv( + className: 'flex flex-row flex-wrap items-center gap-3', + children: [ + for (final ButtonIntent intent in ButtonIntent.values) + Button( + intent: intent, + size: size, + onPressed: () {}, + child: WText(intent.name), + ), + ], + ), + WDiv( + className: 'flex flex-row flex-wrap items-center gap-3', + children: [ + Button( + isLoading: true, + onPressed: () {}, + child: const WText('Loading'), + ), + Button( + disabled: true, + onPressed: () {}, + child: const WText('Disabled'), + ), + ], + ), + ], + ); + } + + /// Every badge tone. + Widget _buildBadges() { + return WDiv( + className: 'flex flex-row flex-wrap items-center gap-3', + children: [ + for (final BadgeTone tone in BadgeTone.values) + Badge(tone.name, tone: tone), + ], + ); + } + + /// Normal and error input states wrapped in a form field. + Widget _buildInputs() { + return const WDiv( + className: 'flex flex-col gap-4 max-w-md', + children: [ + MagicFormField( + label: 'Email', + hint: 'We never share your email.', + child: Input(placeholder: 'ada@example.com'), + ), + MagicFormField( + label: 'Password', + error: 'Password is required.', + child: Input( + placeholder: 'Enter your password', + state: InputState.error, + ), + ), + ], + ); + } + + /// On and off switch states, plus disabled. + Widget _buildSwitches() { + return WDiv( + className: 'flex flex-row items-center gap-6', + children: [ + Switch(value: true, onChanged: (_) {}), + Switch(value: false, onChanged: (_) {}), + const Switch(value: true, onChanged: null, disabled: true), + ], + ); + } + + /// A single-select dropdown with a chosen value. + Widget _buildSelect() { + return WDiv( + className: 'max-w-xs', + child: Select( + value: 'engineering', + onChange: (_) {}, + options: const >[ + SelectOption(value: 'engineering', label: 'Engineering'), + SelectOption(value: 'design', label: 'Design'), + SelectOption(value: 'personal', label: 'Personal'), + ], + ), + ); + } + + /// A tab strip with the second tab active. + Widget _buildTabs() { + return Tabs( + tabs: const ['Overview', 'Members', 'Settings'], + selectedIndex: 1, + onChanged: (_) {}, + panelBuilder: (int index) => WDiv( + className: 'p-4', + child: WText('Panel ${index + 1}', className: 'text-fg text-sm'), + ), + ); + } + + /// Each card variant with a title and body. + Widget _buildCards() { + return WDiv( + className: 'flex flex-row flex-wrap gap-4', + children: [ + for (final CardVariant variant in CardVariant.values) + WDiv( + className: 'w-64', + child: Card( + title: variant.name, + variant: variant, + child: const Typography( + 'Card body content.', + variant: TypographyVariant.caption, + ), + ), + ), + ], + ); + } +} diff --git a/lib/preview/dashboard_screen.preview.dart b/lib/preview/dashboard_screen.preview.dart new file mode 100644 index 0000000..774dd54 --- /dev/null +++ b/lib/preview/dashboard_screen.preview.dart @@ -0,0 +1,22 @@ +import 'package:flutter/widgets.dart'; + +import '../resources/views/dashboard_view.dart'; +import 'preview_mock_harness.dart'; +import 'screen_preview_scaffold.dart'; + +/// Feature-screen preview: the app dashboard landing view, authenticated and +/// backend-free. +class DashboardScreenPreview extends StatelessWidget { + /// Creates the dashboard screen preview. + const DashboardScreenPreview({super.key}); + + @override + Widget build(BuildContext context) { + return const ScreenPreviewScaffold( + state: PreviewState.success, + builder: _build, + ); + } + + static Widget _build(BuildContext context) => const DashboardView(); +} diff --git a/lib/preview/foundations.preview.dart b/lib/preview/foundations.preview.dart new file mode 100644 index 0000000..423503d --- /dev/null +++ b/lib/preview/foundations.preview.dart @@ -0,0 +1,137 @@ +import 'package:flutter/widgets.dart'; +import 'package:magic/magic.dart'; +import 'package:magic_starter/magic_starter.dart' + show Typography, TypographyVariant; + +/// Foundations preview: the design-token vocabulary the whole system is built +/// on, rendered as live swatches so the catalog shows colors and type next to +/// the components that consume them. +/// +/// Every swatch is painted purely through the 17 semantic alias keys (no raw +/// hex), so it doubles as a visual assertion that the generated theme resolves +/// each role in both light and dark. +class FoundationsPreview extends StatelessWidget { + /// Creates the foundations preview. + const FoundationsPreview({super.key}); + + /// The semantic surface/background roles and their token classNames. + static const List<(String, String)> _surfaceTokens = <(String, String)>[ + ('surface', 'bg-surface'), + ('surface-container', 'bg-surface-container'), + ('surface-container-high', 'bg-surface-container-high'), + ('primary', 'bg-primary'), + ('primary-container', 'bg-primary-container'), + ('accent', 'bg-accent'), + ('destructive', 'bg-destructive'), + ('destructive-container', 'bg-destructive-container'), + ('success', 'bg-success'), + ('warning', 'bg-warning'), + ]; + + /// The foreground/text roles and their token classNames. + static const List<(String, String)> _foregroundTokens = <(String, String)>[ + ('fg', 'text-fg'), + ('fg-muted', 'text-fg-muted'), + ('fg-disabled', 'text-fg-disabled'), + ('on-primary', 'text-on-primary'), + ('on-destructive', 'text-on-destructive'), + ]; + + /// The border roles and their token classNames. + static const List<(String, String)> _borderTokens = <(String, String)>[ + ('border', 'border-color-border'), + ('border-subtle', 'border-color-border-subtle'), + ]; + + @override + Widget build(BuildContext context) { + return WDiv( + className: 'flex flex-col gap-8', + children: [ + _section('Colors: surfaces', _buildSurfaceSwatches()), + _section('Colors: foreground', _buildForegroundSwatches()), + _section('Colors: borders', _buildBorderSwatches()), + _section('Typography', _buildTypeScale()), + ], + ); + } + + /// A labelled section wrapper. + Widget _section(String title, Widget body) { + return WDiv( + className: 'flex flex-col gap-3', + children: [ + WText(title, className: 'text-fg text-sm font-semibold uppercase'), + body, + ], + ); + } + + /// Surface swatches: a filled tile per background role. + Widget _buildSurfaceSwatches() { + return WDiv( + className: 'flex flex-row flex-wrap gap-3', + children: [ + for (final (String name, String token) in _surfaceTokens) + WDiv( + className: 'flex flex-col gap-1 w-36', + children: [ + WDiv( + className: '$token h-16 rounded-lg border border-color-border', + ), + WText(name, className: 'text-fg-muted text-xs'), + ], + ), + ], + ); + } + + /// Foreground swatches: each role's text painted on its natural backdrop. + Widget _buildForegroundSwatches() { + return WDiv( + className: 'flex flex-row flex-wrap gap-3', + children: [ + for (final (String name, String token) in _foregroundTokens) + WDiv( + className: token.startsWith('text-on-') + ? (token == 'text-on-primary' + ? 'bg-primary px-4 py-3 rounded-lg w-44' + : 'bg-destructive px-4 py-3 rounded-lg w-44') + : 'bg-surface-container px-4 py-3 rounded-lg w-44', + child: WText(name, className: '$token text-sm font-medium'), + ), + ], + ); + } + + /// Border swatches: each border role around a neutral tile. + Widget _buildBorderSwatches() { + return WDiv( + className: 'flex flex-row flex-wrap gap-3', + children: [ + for (final (String name, String token) in _borderTokens) + WDiv( + className: 'flex flex-col gap-1 w-36', + children: [ + WDiv(className: 'bg-surface h-16 rounded-lg border-2 $token'), + WText(name, className: 'text-fg-muted text-xs'), + ], + ), + ], + ); + } + + /// Typography scale rendered with the Typography component. + Widget _buildTypeScale() { + return const WDiv( + className: 'flex flex-col gap-2', + children: [ + Typography('Heading 1', variant: TypographyVariant.h1), + Typography('Heading 2', variant: TypographyVariant.h2), + Typography('Heading 3', variant: TypographyVariant.h3), + Typography('Body text renders the default paragraph style.'), + Typography('Caption text', variant: TypographyVariant.caption), + ], + ); + } +} diff --git a/lib/preview/login_screen.preview.dart b/lib/preview/login_screen.preview.dart new file mode 100644 index 0000000..03c0772 --- /dev/null +++ b/lib/preview/login_screen.preview.dart @@ -0,0 +1,21 @@ +import 'package:flutter/widgets.dart'; +import 'package:magic_starter/magic_starter.dart' show MagicStarterLoginView; + +import 'preview_mock_harness.dart'; +import 'screen_preview_scaffold.dart'; + +/// Feature-screen preview: the magic_starter login view rendered backend-free. +class LoginScreenPreview extends StatelessWidget { + /// Creates the login screen preview. + const LoginScreenPreview({super.key}); + + @override + Widget build(BuildContext context) { + return const ScreenPreviewScaffold( + state: PreviewState.success, + builder: _build, + ); + } + + static Widget _build(BuildContext context) => const MagicStarterLoginView(); +} diff --git a/lib/preview/preview_mock_harness.dart b/lib/preview/preview_mock_harness.dart new file mode 100644 index 0000000..e52f2eb --- /dev/null +++ b/lib/preview/preview_mock_harness.dart @@ -0,0 +1,292 @@ +import 'dart:async'; + +import 'package:flutter/widgets.dart' show WidgetsBinding; +import 'package:magic/magic.dart'; +import 'package:magic_starter/magic_starter.dart'; + +/// The render state a feature-screen preview should portray. +/// +/// Feature views are controller-backed, so the catalog drives them through the +/// network layer: [success] resolves canned data immediately, [loading] never +/// resolves (the view stays on its spinner), and [error] resolves a non-2xx +/// response so the view shows its error branch. No backend is ever contacted. +enum PreviewState { + /// Requests resolve with sample data so the view renders its filled state. + success, + + /// Requests never resolve so the view stays on its loading indicator. + loading, + + /// Requests resolve with a 500 so the view renders its error branch. + error, +} + +/// Sample backend-free data shared by every feature-screen preview. +/// +/// These maps mirror the `api/v1` resource shapes the real backend returns, so +/// the magic_starter views and controllers render exactly as they would in +/// production without a running server. +final class PreviewSampleData { + PreviewSampleData._(); + + /// A representative authenticated user. + static Map get user => { + 'id': 1, + 'name': 'Ada Lovelace', + 'email': 'ada@example.com', + 'email_verified_at': '2026-01-01T00:00:00.000Z', + 'two_factor_enabled': true, + 'current_team_id': 10, + 'profile_photo_url': null, + 'created_at': '2026-01-01T00:00:00.000Z', + }; + + /// A representative set of teams the sample user belongs to. + static List> get teams => >[ + { + 'id': 10, + 'name': 'Engineering', + 'personal_team': false, + 'role': 'owner', + }, + { + 'id': 11, + 'name': 'Design', + 'personal_team': false, + 'role': 'admin', + }, + { + 'id': 12, + 'name': 'Personal', + 'personal_team': true, + 'role': 'owner', + }, + ]; + + /// A representative notifications page. + static List> get notifications => >[ + { + 'id': 'n1', + 'type': 'team.invitation', + 'data': { + 'title': 'Team invitation', + 'body': 'You were invited to join Design.', + }, + 'read_at': null, + 'created_at': '2026-06-20T10:00:00.000Z', + }, + { + 'id': 'n2', + 'type': 'profile.updated', + 'data': { + 'title': 'Profile updated', + 'body': 'Your profile changes were saved.', + }, + 'read_at': '2026-06-19T08:00:00.000Z', + 'created_at': '2026-06-19T08:00:00.000Z', + }, + ]; +} + +/// A contract-mock network driver for the preview catalog. +/// +/// It mirrors the `MockNetworkDriver implements NetworkDriver` pattern the +/// magic_starter test suite uses (tracking [lastMethod]/[lastUrl]/[lastData]), +/// but instead of single-response queueing it answers every request from a +/// [PreviewState], so a preview can render loading, success, or error without a +/// backend. In [PreviewState.loading] requests return a [Future] that never +/// completes, leaving controller-backed views on their spinner. +final class PreviewMockNetworkDriver implements NetworkDriver { + /// Creates a driver answering every request according to [state]. + PreviewMockNetworkDriver(this.state); + + /// The state every response is shaped to portray. + final PreviewState state; + + /// The HTTP verb of the most recent request (diagnostic parity with tests). + String? lastMethod; + + /// The URL of the most recent request. + String? lastUrl; + + /// The payload of the most recent request. + dynamic lastData; + + /// Resolve a request for [url] to a canned response for the current [state]. + Future _respond(String method, String url, {dynamic data}) { + lastMethod = method; + lastUrl = url; + lastData = data; + + // 1. Loading: never resolve so the view stays on its loading indicator. + if (state == PreviewState.loading) { + return Completer().future; + } + + // 2. Error: a non-2xx response drives every view to its error branch. + if (state == PreviewState.error) { + return Future.delayed( + Duration.zero, + () => MagicResponse( + data: {'message': 'Preview error state.'}, + statusCode: 500, + message: 'Preview error state.', + ), + ); + } + + // 3. Success: route-shaped sample data. Resolved on the event queue + // (Future.delayed, not Future.value) so a controller that fires its + // fetch during the view's first build receives the response AFTER the + // frame; a synchronously-completed future would deliver the result + // mid-build and trip "setState() called during build" in the view. + return Future.delayed(Duration.zero, () => _successFor(url)); + } + + /// Build a 200 response whose body matches the resource [url]. + MagicResponse _successFor(String url) { + final body = {'data': _dataFor(url)}; + return MagicResponse(data: body, statusCode: 200); + } + + /// Pick the sample payload for a given [url]. + dynamic _dataFor(String url) { + if (url.contains('teams')) return PreviewSampleData.teams; + if (url.contains('notifications')) return PreviewSampleData.notifications; + if (url.contains('login') || url.contains('register')) { + return { + 'token': 'preview-token', + 'user': PreviewSampleData.user, + }; + } + return PreviewSampleData.user; + } + + @override + void addInterceptor(MagicNetworkInterceptor interceptor) {} + + @override + Future delete(String url, {Map? headers}) => + _respond('DELETE', url); + + @override + Future destroy( + String resource, + String id, { + Map? headers, + }) => _respond('DESTROY', '$resource/$id'); + + @override + Future get( + String url, { + Map? query, + Map? headers, + }) => _respond('GET', url); + + @override + Future index( + String resource, { + Map? filters, + Map? headers, + }) => _respond('INDEX', resource); + + @override + Future post( + String url, { + dynamic data, + Map? headers, + }) => _respond('POST', url, data: data); + + @override + Future put( + String url, { + dynamic data, + Map? headers, + }) => _respond('PUT', url, data: data); + + @override + Future show( + String resource, + String id, { + Map? headers, + }) => _respond('SHOW', '$resource/$id'); + + @override + Future store( + String resource, + Map data, { + Map? headers, + }) => _respond('STORE', resource, data: data); + + @override + Future update( + String resource, + String id, + Map data, { + Map? headers, + }) => _respond('UPDATE', '$resource/$id', data: data); + + @override + Future upload( + String url, { + required Map data, + required Map files, + Map? headers, + }) => _respond('UPLOAD', url, data: data); +} + +/// Installs a backend-free environment for the feature-screen previews. +/// +/// The catalog renders previews inside the already-booted app, so this harness +/// rebinds the `network` singleton to a [PreviewMockNetworkDriver] for the +/// requested [PreviewState] and seeds [Auth] with a sample user so +/// authenticated views (dashboard, profile, teams) render their filled state. +/// It is idempotent per-state: re-installing the same state is a no-op, so the +/// preview builders can call it on every `build()` without churning the +/// singleton or the auth session. +/// +/// This only ever runs from a `*.preview.dart` builder, which is reachable only +/// through the dev-only `/preview` boundary; it never touches a release build. +final class PreviewMockHarness { + PreviewMockHarness._(); + + static PreviewState? _installed; + + /// Bind the mock network for [state] and seed the sample auth session. + /// + /// Returns the bound driver so a caller can inspect the recorded request. + static PreviewMockNetworkDriver install(PreviewState state) { + final driver = PreviewMockNetworkDriver(state); + + // 1. Rebind the network layer so every controller request is mocked. + Magic.singleton('network', () => driver); + + // 2. Seed the authenticated session once so authenticated views render + // their filled state. Deferred to a post-frame callback: install() is + // called from a preview builder DURING the build phase, and Auth.login + // notifies its state listeners synchronously, which would trigger + // "setState() called during build". Running it after the frame seeds the + // session and rebuilds cleanly. Errors are swallowed deliberately: a + // preview must render even if the guard cannot persist a token (e.g. + // secure storage is unavailable on the preview platform). + if (_installed != state) { + _installed = state; + WidgetsBinding.instance.addPostFrameCallback( + (_) => unawaited(_seedAuth()), + ); + } + + return driver; + } + + /// Seed [Auth] with the sample user so authenticated previews render filled. + static Future _seedAuth() async { + try { + final user = MagicStarter.createUser(PreviewSampleData.user); + await Auth.login({'token': 'preview-token'}, user); + } catch (_) { + // Preview-only: a failed seed leaves the view in its guest/empty state, + // which is still a valid thing to preview. + } + } +} diff --git a/lib/preview/profile_screen.preview.dart b/lib/preview/profile_screen.preview.dart new file mode 100644 index 0000000..90ba246 --- /dev/null +++ b/lib/preview/profile_screen.preview.dart @@ -0,0 +1,24 @@ +import 'package:flutter/widgets.dart'; +import 'package:magic_starter/magic_starter.dart' + show MagicStarterProfileSettingsView; + +import 'preview_mock_harness.dart'; +import 'screen_preview_scaffold.dart'; + +/// Feature-screen preview: the profile settings view with a sample user, +/// rendered backend-free. +class ProfileScreenPreview extends StatelessWidget { + /// Creates the profile screen preview. + const ProfileScreenPreview({super.key}); + + @override + Widget build(BuildContext context) { + return const ScreenPreviewScaffold( + state: PreviewState.success, + builder: _build, + ); + } + + static Widget _build(BuildContext context) => + const MagicStarterProfileSettingsView(); +} diff --git a/lib/preview/register_screen.preview.dart b/lib/preview/register_screen.preview.dart new file mode 100644 index 0000000..62068d2 --- /dev/null +++ b/lib/preview/register_screen.preview.dart @@ -0,0 +1,23 @@ +import 'package:flutter/widgets.dart'; +import 'package:magic_starter/magic_starter.dart' show MagicStarterRegisterView; + +import 'preview_mock_harness.dart'; +import 'screen_preview_scaffold.dart'; + +/// Feature-screen preview: the magic_starter register view rendered +/// backend-free. +class RegisterScreenPreview extends StatelessWidget { + /// Creates the register screen preview. + const RegisterScreenPreview({super.key}); + + @override + Widget build(BuildContext context) { + return const ScreenPreviewScaffold( + state: PreviewState.success, + builder: _build, + ); + } + + static Widget _build(BuildContext context) => + const MagicStarterRegisterView(); +} diff --git a/lib/preview/screen_preview_scaffold.dart b/lib/preview/screen_preview_scaffold.dart new file mode 100644 index 0000000..80511d4 --- /dev/null +++ b/lib/preview/screen_preview_scaffold.dart @@ -0,0 +1,78 @@ +import 'package:flutter/widgets.dart'; +import 'package:magic/magic.dart'; + +import 'preview_mock_harness.dart'; + +/// Shared scaffold for feature-screen previews. +/// +/// Installs the [PreviewMockHarness] for the requested [state] (so the wrapped +/// controller-backed view renders backend-free), simulates a phone viewport so +/// the view renders its mobile layout, and DEFERS the view mount by one frame. +/// +/// The deferral matters: the catalog builds a preview's body synchronously +/// inside its own `build()`. A magic_starter feature view is controller-backed +/// (`MagicStatefulView`), and binding its controller on mount notifies state +/// listeners; mounting it DURING the catalog build trips "setState() called +/// during build". Mounting it one frame later (after a post-frame callback) +/// moves the controller bind out of the build phase, so the preview is clean. +/// The placeholder shown for that single frame keeps the layout from jumping. +class ScreenPreviewScaffold extends StatefulWidget { + /// Wraps [builder]'s output, installing the harness for [state] first. + const ScreenPreviewScaffold({ + super.key, + required this.state, + required this.builder, + }); + + /// The state the harness should portray (success, loading, or error). + final PreviewState state; + + /// Builds the feature view to preview. + final WidgetBuilder builder; + + @override + State createState() => _ScreenPreviewScaffoldState(); +} + +class _ScreenPreviewScaffoldState extends State { + /// The simulated phone viewport for feature-screen previews. + static const Size _phoneViewport = Size(390, 844); + + bool _mounted = false; + + @override + void initState() { + super.initState(); + // Bind the mock network + sample auth session for this state before the + // view mounts. Idempotent per-state, so it does not churn the singleton. + PreviewMockHarness.install(widget.state); + // Mount the controller-backed view one frame later (see the class doc): it + // must not build during the catalog's build phase. + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted) setState(() => _mounted = true); + }); + } + + @override + Widget build(BuildContext context) { + // Simulate a phone viewport. Wind resolves its responsive breakpoints + // (sm:/md:/lg:) from MediaQuery.size.width (wind_helpers.dart), NOT the + // local box constraints. Without this override a feature screen reads the + // full browser width, renders its DESKTOP layout, and overflows the narrow + // side-by-side pane. A phone-sized MediaQuery makes it render its MOBILE + // layout, which fits the constrained width with no RenderFlex overflow. + return MediaQuery( + data: MediaQuery.of(context).copyWith(size: _phoneViewport), + child: WDiv( + className: 'w-full max-w-[390px] mx-auto', + child: SingleChildScrollView( + // First frame: a sized placeholder so the column does not jump when + // the deferred view mounts. Subsequent frames: the real view. + child: _mounted + ? Builder(builder: widget.builder) + : const SizedBox(height: 320), + ), + ), + ); + } +} diff --git a/lib/preview/settings_screen.preview.dart b/lib/preview/settings_screen.preview.dart new file mode 100644 index 0000000..7185dbd --- /dev/null +++ b/lib/preview/settings_screen.preview.dart @@ -0,0 +1,24 @@ +import 'package:flutter/widgets.dart'; +import 'package:magic_starter/magic_starter.dart' + show MagicStarterTeamSettingsView; + +import 'preview_mock_harness.dart'; +import 'screen_preview_scaffold.dart'; + +/// Feature-screen preview: the team settings view, authenticated and +/// backend-free. +class SettingsScreenPreview extends StatelessWidget { + /// Creates the settings screen preview. + const SettingsScreenPreview({super.key}); + + @override + Widget build(BuildContext context) { + return const ScreenPreviewScaffold( + state: PreviewState.success, + builder: _build, + ); + } + + static Widget _build(BuildContext context) => + const MagicStarterTeamSettingsView(); +} diff --git a/lib/preview/teams_screen.preview.dart b/lib/preview/teams_screen.preview.dart new file mode 100644 index 0000000..c852269 --- /dev/null +++ b/lib/preview/teams_screen.preview.dart @@ -0,0 +1,24 @@ +import 'package:flutter/widgets.dart'; +import 'package:magic_starter/magic_starter.dart' + show MagicStarterTeamCreateView; + +import 'preview_mock_harness.dart'; +import 'screen_preview_scaffold.dart'; + +/// Feature-screen preview: the team create view, authenticated and +/// backend-free. +class TeamsScreenPreview extends StatelessWidget { + /// Creates the teams screen preview. + const TeamsScreenPreview({super.key}); + + @override + Widget build(BuildContext context) { + return const ScreenPreviewScaffold( + state: PreviewState.success, + builder: _build, + ); + } + + static Widget _build(BuildContext context) => + const MagicStarterTeamCreateView(); +} diff --git a/lib/resources/views/dashboard_view.dart b/lib/resources/views/dashboard_view.dart new file mode 100644 index 0000000..ed43b0e --- /dev/null +++ b/lib/resources/views/dashboard_view.dart @@ -0,0 +1,158 @@ +import 'package:flutter/material.dart' show Icons; +import 'package:flutter/widgets.dart'; +import 'package:magic/magic.dart'; +import 'package:magic_starter/magic_starter.dart' + show Card, CardVariant, Typography, TypographyVariant; + +/// Dashboard view: the default landing page after successful authentication. +/// +/// Design-first: every surface and text color flows through the semantic +/// alias tokens (`bg-surface`, `text-fg`, ...) so it tracks DESIGN.md in both +/// light and dark. The quick-link tiles compose the shared [Card] component. +class DashboardView extends StatelessWidget { + /// Creates the [DashboardView]. + const DashboardView({super.key}); + + static const _iconHero = Icons.auto_awesome; + static const _iconDocs = Icons.menu_book; + static const _iconGitHub = Icons.code; + static const _iconCli = Icons.terminal; + static const _iconArrow = Icons.arrow_forward_outlined; + static const _iconHeart = Icons.favorite; + + @override + Widget build(BuildContext context) { + final appName = Config.get('app.name', 'My App') ?? 'My App'; + + return WDiv( + className: 'w-full max-w-[480px] md:max-w-4xl mx-auto p-4 lg:p-8', + child: WDiv( + className: ''' + rounded-2xl bg-surface-container + border border-color-border + p-6 lg:p-8 flex flex-col items-center + ''', + children: [ + // 1. Hero. + WDiv( + className: ''' + w-20 h-20 rounded-2xl + flex items-center justify-center + bg-primary + ''', + child: const WIcon( + _iconHero, + className: 'text-4xl text-on-primary', + ), + ), + const WSpacer(className: 'h-6'), + Typography( + appName, + variant: TypographyVariant.h2, + className: 'text-center', + ), + const WSpacer(className: 'h-2'), + const Typography( + 'Built with Magic Starter', + variant: TypographyVariant.caption, + ), + + const WSpacer(className: 'h-8'), + + // 2. Quick-link cards. + WDiv( + className: 'w-full grid grid-cols-1 md:grid-cols-3 gap-3', + children: [ + _buildLinkCard( + icon: _iconDocs, + title: 'Documentation', + description: 'Read the Magic Framework docs to get started.', + url: 'https://magic.fluttersdk.com', + ), + _buildLinkCard( + icon: _iconGitHub, + title: 'GitHub', + description: + 'Star the repo, report issues, or contribute code.', + url: 'https://github.com/fluttersdk/magic', + ), + _buildLinkCard( + icon: _iconCli, + title: 'CLI Commands', + description: + 'Run `magic --help` to see all available commands.', + url: 'https://magic.fluttersdk.com/cli', + ), + ], + ), + + const WSpacer(className: 'h-8'), + + // 3. Footer. + WDiv( + className: 'flex flex-row items-center justify-center gap-1', + children: [ + const WText('Made with', className: 'text-xs text-fg-muted'), + const WIcon(_iconHeart, className: 'text-xs text-destructive'), + const WText('by', className: 'text-xs text-fg-muted'), + WAnchor( + onTap: () => Launch.url('https://anilcancakir.com'), + child: const WText( + 'Anılcan Çakır', + className: 'text-xs font-medium text-fg', + ), + ), + ], + ), + ], + ), + ); + } + + /// Builds a single quick-link tile composing the shared [Card] component. + Widget _buildLinkCard({ + required IconData icon, + required String title, + required String description, + required String url, + }) { + return Card( + variant: CardVariant.inset, + child: WDiv( + className: 'flex flex-col', + children: [ + WDiv( + className: 'flex flex-row items-center gap-3 mb-2', + children: [ + WDiv( + className: 'p-2 rounded-lg bg-primary-container', + child: WIcon(icon, className: 'text-lg text-fg'), + ), + WText( + title, + className: 'text-base font-semibold text-fg', + ), + ], + ), + WText( + description, + className: 'text-sm text-fg-muted mb-3', + ), + WAnchor( + onTap: () => Launch.url(url), + child: WDiv( + className: 'flex flex-row items-center gap-1', + children: [ + const WText( + 'Learn more', + className: 'text-sm font-medium text-fg', + ), + const WIcon(_iconArrow, className: 'text-sm text-fg'), + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/resources/views/welcome_view.dart b/lib/resources/views/welcome_view.dart new file mode 100644 index 0000000..c94cd9e --- /dev/null +++ b/lib/resources/views/welcome_view.dart @@ -0,0 +1,167 @@ +import 'package:flutter/material.dart' show Icons; +import 'package:flutter/widgets.dart'; +import 'package:magic/magic.dart'; +import 'package:magic_starter/magic_starter.dart' + show Card, CardVariant, Typography, TypographyVariant; + +/// Welcome view: the default landing page for a new Magic application. +/// +/// Design-first: colors flow through the semantic alias tokens so the screen +/// tracks DESIGN.md in light and dark. Kept as a self-contained centered shell +/// (it is the pre-auth landing, rendered outside the app layout). +class WelcomeView extends StatelessWidget { + /// Creates the [WelcomeView]. + const WelcomeView({super.key}); + + static const _iconHero = Icons.auto_awesome; + static const _iconDocs = Icons.menu_book; + static const _iconGitHub = Icons.code; + static const _iconCli = Icons.terminal; + static const _iconArrow = Icons.arrow_forward_outlined; + static const _iconHeart = Icons.favorite; + + @override + Widget build(BuildContext context) { + final appName = Config.get('app.name', '') ?? ''; + + return WDiv( + className: 'bg-surface min-h-screen w-full', + child: SingleChildScrollView( + child: WDiv( + className: 'w-full max-w-[480px] mx-auto p-4 lg:p-8', + child: WDiv( + className: ''' + rounded-2xl bg-surface-container + border border-color-border + p-6 lg:p-8 flex flex-col items-center + ''', + children: [ + // 1. Hero. + WDiv( + className: ''' + w-20 h-20 rounded-2xl + flex items-center justify-center + bg-primary + ''', + child: const WIcon( + _iconHero, + className: 'text-on-primary text-4xl', + ), + ), + const WSpacer(className: 'h-6'), + Typography( + appName, + variant: TypographyVariant.h2, + className: 'text-center', + ), + const WSpacer(className: 'h-2'), + const Typography( + 'Built with Magic Framework', + variant: TypographyVariant.caption, + ), + + const WSpacer(className: 'h-8'), + + // 2. Quick-link cards. + WDiv( + className: 'w-full flex flex-col gap-3', + children: [ + _buildLinkCard( + icon: _iconDocs, + title: 'Documentation', + description: + 'Read the Magic Framework docs to get started.', + url: 'https://magic.fluttersdk.com', + ), + _buildLinkCard( + icon: _iconGitHub, + title: 'GitHub', + description: + 'Star the repo, report issues, or contribute code.', + url: 'https://github.com/fluttersdk/magic', + ), + _buildLinkCard( + icon: _iconCli, + title: 'CLI Commands', + description: + 'Run `magic --help` to see all available commands.', + url: 'https://magic.fluttersdk.com/packages/magic-cli', + ), + ], + ), + + const WSpacer(className: 'h-8'), + + // 3. Footer. + WDiv( + className: 'flex flex-row items-center justify-center gap-1', + children: [ + const WText('Made with', className: 'text-xs text-fg-muted'), + const WIcon( + _iconHeart, + className: 'text-xs text-destructive', + ), + const WText('by', className: 'text-xs text-fg-muted'), + WAnchor( + onTap: () => Launch.url('https://anilcancakir.com'), + child: const WText( + 'Anılcan Çakır', + className: 'text-xs font-medium text-fg', + ), + ), + ], + ), + ], + ), + ), + ), + ); + } + + /// Builds a single quick-link tile composing the shared [Card] component. + Widget _buildLinkCard({ + required IconData icon, + required String title, + required String description, + required String url, + }) { + return Card( + variant: CardVariant.inset, + child: WDiv( + className: 'flex flex-col', + children: [ + WDiv( + className: 'flex flex-row items-center gap-3 mb-2', + children: [ + WDiv( + className: 'p-2 rounded-lg bg-primary-container', + child: WIcon(icon, className: 'text-lg text-fg'), + ), + WText( + title, + className: 'text-base font-semibold text-fg', + ), + ], + ), + WText( + description, + className: 'text-sm text-fg-muted mb-3', + ), + WAnchor( + onTap: () => Launch.url(url), + child: WDiv( + className: 'flex flex-row items-center gap-1', + children: [ + const WText( + 'Learn more', + className: 'text-sm font-medium text-fg', + ), + const WIcon(_iconArrow, className: 'text-sm text-fg'), + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/routes/app.dart b/lib/routes/app.dart new file mode 100644 index 0000000..2978d27 --- /dev/null +++ b/lib/routes/app.dart @@ -0,0 +1,25 @@ +import 'package:magic/magic.dart'; + +// import '../resources/views/welcome_view.dart'; // Replaced by DashboardView +import 'package:magic_starter/magic_starter.dart'; +import '../resources/views/dashboard_view.dart'; + +/// Application Route Definitions. +/// +/// Register all application routes here. This function is called by +/// [RouteServiceProvider.boot()] during the Magic bootstrap lifecycle. +/// +/// See also: `lib/app/kernel.dart` for middleware registration. +void registerAppRoutes() { + // Auth-protected routes with AppLayout + MagicRoute.group( + layout: (child) => MagicStarter.view.makeLayout('layout.app', child: child), + middleware: ['auth'], + layoutId: 'app', + routes: () { + MagicRoute.page('/', () => const DashboardView()); + }, + ); + + // MagicRoute.page('/', () => const WelcomeView()).title('Welcome'); // Replaced by DashboardView +} 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..834ca5c --- /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 "magic_example") +# The unique GTK application identifier for this application. See: +# https://wiki.gnome.org/HowDoI/ChooseApplicationID +set(APPLICATION_ID "com.fluttersdk.magic_example") + +# 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..1f2a361 --- /dev/null +++ b/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,35 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + +#include +#include +#include +#include +#include +#include + +void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) desktop_webview_window_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopWebviewWindowPlugin"); + desktop_webview_window_plugin_register_with_registrar(desktop_webview_window_registrar); + g_autoptr(FlPluginRegistrar) file_selector_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin"); + file_selector_plugin_register_with_registrar(file_selector_linux_registrar); + g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin"); + flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar); + g_autoptr(FlPluginRegistrar) gtk_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "GtkPlugin"); + gtk_plugin_register_with_registrar(gtk_registrar); + g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); + url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); + g_autoptr(FlPluginRegistrar) window_to_front_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "WindowToFrontPlugin"); + window_to_front_plugin_register_with_registrar(window_to_front_registrar); +} 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..db702e7 --- /dev/null +++ b/linux/flutter/generated_plugins.cmake @@ -0,0 +1,30 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + desktop_webview_window + file_selector_linux + flutter_secure_storage_linux + gtk + url_launcher_linux + window_to_front +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST + jni +) + +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..47c9dff --- /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, "magic_example"); + 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, "magic_example"); + } + + 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..4f5c312 --- /dev/null +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,32 @@ +// +// Generated file. Do not edit. +// + +import FlutterMacOS +import Foundation + +import app_links +import desktop_webview_window +import file_picker +import file_selector_macos +import flutter_secure_storage_darwin +import flutter_web_auth_2 +import google_sign_in_ios +import share_plus +import shared_preferences_foundation +import url_launcher_macos +import window_to_front + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin")) + DesktopWebviewWindowPlugin.register(with: registry.registrar(forPlugin: "DesktopWebviewWindowPlugin")) + FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin")) + FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) + FlutterSecureStorageDarwinPlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStorageDarwinPlugin")) + FlutterWebAuth2Plugin.register(with: registry.registrar(forPlugin: "FlutterWebAuth2Plugin")) + FLTGoogleSignInPlugin.register(with: registry.registrar(forPlugin: "FLTGoogleSignInPlugin")) + SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) + SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) + UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) + WindowToFrontPlugin.register(with: registry.registrar(forPlugin: "WindowToFrontPlugin")) +} diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..06e701f --- /dev/null +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,729 @@ +// !$*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 */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; +/* 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 /* magic_example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "magic_example.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 = ""; }; + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; 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 = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, + ); + 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 /* magic_example.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 = ( + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */, + 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; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* magic_example.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; + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */, + ); + 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.fluttersdk.magicExample.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/magic_example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/magic_example"; + }; + 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.fluttersdk.magicExample.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/magic_example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/magic_example"; + }; + 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.fluttersdk.magicExample.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/magic_example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/magic_example"; + }; + 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 */ + +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency 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/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..85eee3f --- /dev/null +++ b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,77 @@ +{ + "pins" : [ + { + "identity" : "app-check", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/app-check.git", + "state" : { + "revision" : "bb4002485ff867768dec13bf904a2ddb050bd1b1", + "version" : "11.3.0" + } + }, + { + "identity" : "appauth-ios", + "kind" : "remoteSourceControl", + "location" : "https://github.com/openid/AppAuth-iOS.git", + "state" : { + "revision" : "a7caeda164dc5108bf4649472b28a5af65dc6f33", + "version" : "2.1.0" + } + }, + { + "identity" : "googlesignin-ios", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleSignIn-iOS.git", + "state" : { + "revision" : "08d8dcecafb575f98879ffdbb8302c1b9ad65d19", + "version" : "9.2.0" + } + }, + { + "identity" : "googleutilities", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleUtilities.git", + "state" : { + "revision" : "c46e5f8b7c23265f17c24ca7f9fa1b13ded7a822", + "version" : "8.1.1" + } + }, + { + "identity" : "gtm-session-fetcher", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/gtm-session-fetcher.git", + "state" : { + "revision" : "a2ab612cb980066ee56d90d60d8462992c07f24b", + "version" : "3.5.0" + } + }, + { + "identity" : "gtmappauth", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GTMAppAuth.git", + "state" : { + "revision" : "56e0ccf09a6dd29dc7e68bdf729598240ca8aa16", + "version" : "5.0.0" + } + }, + { + "identity" : "interop-ios-for-google-sdks", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/interop-ios-for-google-sdks.git", + "state" : { + "revision" : "040d087ac2267d2ddd4cca36c757d1c6a05fdbfe", + "version" : "101.0.0" + } + }, + { + "identity" : "promises", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/promises.git", + "state" : { + "revision" : "f4a19a3c313dc2616c70bb49d29a799fb16be837", + "version" : "2.4.1" + } + } + ], + "version" : 2 +} diff --git a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..2342ab5 --- /dev/null +++ b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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.xcworkspace/xcshareddata/swiftpm/Package.resolved b/macos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..85eee3f --- /dev/null +++ b/macos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,77 @@ +{ + "pins" : [ + { + "identity" : "app-check", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/app-check.git", + "state" : { + "revision" : "bb4002485ff867768dec13bf904a2ddb050bd1b1", + "version" : "11.3.0" + } + }, + { + "identity" : "appauth-ios", + "kind" : "remoteSourceControl", + "location" : "https://github.com/openid/AppAuth-iOS.git", + "state" : { + "revision" : "a7caeda164dc5108bf4649472b28a5af65dc6f33", + "version" : "2.1.0" + } + }, + { + "identity" : "googlesignin-ios", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleSignIn-iOS.git", + "state" : { + "revision" : "08d8dcecafb575f98879ffdbb8302c1b9ad65d19", + "version" : "9.2.0" + } + }, + { + "identity" : "googleutilities", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleUtilities.git", + "state" : { + "revision" : "c46e5f8b7c23265f17c24ca7f9fa1b13ded7a822", + "version" : "8.1.1" + } + }, + { + "identity" : "gtm-session-fetcher", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/gtm-session-fetcher.git", + "state" : { + "revision" : "a2ab612cb980066ee56d90d60d8462992c07f24b", + "version" : "3.5.0" + } + }, + { + "identity" : "gtmappauth", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GTMAppAuth.git", + "state" : { + "revision" : "56e0ccf09a6dd29dc7e68bdf729598240ca8aa16", + "version" : "5.0.0" + } + }, + { + "identity" : "interop-ios-for-google-sdks", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/interop-ios-for-google-sdks.git", + "state" : { + "revision" : "040d087ac2267d2ddd4cca36c757d1c6a05fdbfe", + "version" : "101.0.0" + } + }, + { + "identity" : "promises", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/promises.git", + "state" : { + "revision" : "f4a19a3c313dc2616c70bb49d29a799fb16be837", + "version" : "2.4.1" + } + } + ], + "version" : 2 +} 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macos/Runner/Configs/AppInfo.xcconfig b/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 0000000..5411389 --- /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 = magic_example + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.fluttersdk.magicExample + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2026 com.fluttersdk. 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..d769771 --- /dev/null +++ b/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,16 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + com.apple.security.network.client + + keychain-access-groups + + + 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..225aa48 --- /dev/null +++ b/macos/Runner/Release.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.network.client + + keychain-access-groups + + + 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..7f53d66 --- /dev/null +++ b/pubspec.lock @@ -0,0 +1,1276 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + app_links: + dependency: transitive + description: + name: app_links + sha256: "9d3c82f634c7f5b5c752f7ee46b67724246043f5e1d5fc1b433dd5b38d780dbe" + url: "https://pub.dev" + source: hosted + version: "7.2.0" + app_links_linux: + dependency: transitive + description: + name: app_links_linux + sha256: f5f7173a78609f3dfd4c2ff2c95bd559ab43c80a87dc6a095921d96c05688c81 + url: "https://pub.dev" + source: hosted + version: "1.0.3" + app_links_platform_interface: + dependency: transitive + description: + name: app_links_platform_interface + sha256: "78a18580eecac98108d1eef52a7db668bc317714f5205e616973363326efe333" + url: "https://pub.dev" + source: hosted + version: "2.0.3" + app_links_web: + dependency: transitive + description: + name: app_links_web + sha256: af060ed76183f9e2b87510a9480e56a5352b6c249778d07bd2c95fc35632a555 + url: "https://pub.dev" + source: hosted + version: "1.0.4" + archive: + dependency: transitive + description: + name: archive + sha256: a96e8b390886ee8abb49b7bd3ac8df6f451c621619f52a26e815fdcf568959ff + url: "https://pub.dev" + source: hosted + version: "4.0.9" + args: + dependency: transitive + description: + name: args + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 + url: "https://pub.dev" + source: hosted + version: "2.7.0" + asn1lib: + dependency: transitive + description: + name: asn1lib + sha256: "9a8f69025044eb466b9b60ef3bc3ac99b4dc6c158ae9c56d25eeccf5bc56d024" + url: "https://pub.dev" + source: hosted + version: "1.6.5" + 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" + characters: + dependency: transitive + description: + name: characters + sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b + url: "https://pub.dev" + source: hosted + version: "1.4.1" + clock: + dependency: transitive + description: + name: clock + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + url: "https://pub.dev" + source: hosted + version: "1.1.2" + code_assets: + dependency: transitive + description: + name: code_assets + sha256: bf394f466ba9205f1812a0433b392d6af280f155f56651eda7c18cc32ed493b8 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + collection: + dependency: transitive + 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" + cross_file: + dependency: transitive + description: + name: cross_file + sha256: "28bb3ae56f117b5aec029d702a90f57d285cd975c3c5c281eaca38dbc47c5937" + url: "https://pub.dev" + source: hosted + version: "0.3.5+2" + 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" + dart_mcp: + dependency: transitive + description: + name: dart_mcp + sha256: c133c6f3b9668ed5e674bbf6f29a792f3abed9aeba53ec391c92f65f248acddf + url: "https://pub.dev" + source: hosted + version: "0.5.1" + dbus: + dependency: transitive + description: + name: dbus + sha256: "0ce9b0a839e6dee59a37a623d2fc26a35bbbe6404213e419b0d6411023d62645" + url: "https://pub.dev" + source: hosted + version: "0.7.14" + desktop_webview_window: + dependency: transitive + description: + name: desktop_webview_window + sha256: b6fdae2cbf9571879b1761c12f27facaf82e22d0bdc74d049907c2a09a432957 + url: "https://pub.dev" + source: hosted + version: "0.3.0" + dio: + dependency: transitive + description: + name: dio + sha256: aff32c08f92787a557dd5c0145ac91536481831a01b4648136373cddb0e64f8c + url: "https://pub.dev" + source: hosted + version: "5.9.2" + dio_web_adapter: + dependency: transitive + description: + name: dio_web_adapter + sha256: "2f9e64323a7c3c7ef69567d5c800424a11f8337b8b228bad02524c9fb3c1f340" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + encrypt: + dependency: transitive + description: + name: encrypt + sha256: "62d9aa4670cc2a8798bab89b39fc71b6dfbacf615de6cf5001fb39f7e4a996a2" + url: "https://pub.dev" + source: hosted + version: "5.0.3" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + url: "https://pub.dev" + source: hosted + version: "1.3.3" + faker: + dependency: transitive + description: + name: faker + sha256: "544c34e9e1d322824156d5a8d451bc1bb778263b892aded24ec7ba77b0706624" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + 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" + file_picker: + dependency: transitive + description: + name: file_picker + sha256: f13a03000d942e476bc1ff0a736d2e9de711d2f89a95cd4c1d88f861c3348387 + url: "https://pub.dev" + source: hosted + version: "11.0.2" + file_selector_linux: + dependency: transitive + description: + name: file_selector_linux + sha256: "2567f398e06ac72dcf2e98a0c95df2a9edd03c2c2e0cacd4780f20cdf56263a0" + url: "https://pub.dev" + source: hosted + version: "0.9.4" + file_selector_macos: + dependency: transitive + description: + name: file_selector_macos + sha256: "5e0bbe9c312416f1787a68259ea1505b52f258c587f12920422671807c4d618a" + url: "https://pub.dev" + source: hosted + version: "0.9.5" + file_selector_platform_interface: + dependency: transitive + description: + name: file_selector_platform_interface + sha256: "35e0bd61ebcdb91a3505813b055b09b79dfdc7d0aee9c09a7ba59ae4bb13dc85" + url: "https://pub.dev" + source: hosted + version: "2.7.0" + file_selector_windows: + dependency: transitive + description: + name: file_selector_windows + sha256: "62197474ae75893a62df75939c777763d39c2bc5f73ce5b88497208bc269abfd" + url: "https://pub.dev" + source: hosted + version: "0.9.3+5" + 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_dotenv: + dependency: transitive + description: + name: flutter_dotenv + sha256: d41da11fb497314fbf89811ec30af02d1d898b47980a129f0a8c0a1720460ba2 + url: "https://pub.dev" + source: hosted + version: "6.0.1" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1" + url: "https://pub.dev" + source: hosted + version: "6.0.0" + flutter_localizations: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + sha256: "3854fe5e3bff0b113c658f260b90c95dea17c92db0f2addeac2e343dd9969785" + url: "https://pub.dev" + source: hosted + version: "2.0.35" + flutter_secure_storage: + dependency: transitive + description: + name: flutter_secure_storage + sha256: "7686b1d6a29985dcbb808c59518226e603e3bfa7c0ddfd1a0d00e4cda77c868e" + url: "https://pub.dev" + source: hosted + version: "10.3.1" + flutter_secure_storage_darwin: + dependency: transitive + description: + name: flutter_secure_storage_darwin + sha256: "82329fa5cdf343773b1b6897dea959105a29f092454259edff92f9f6637e8149" + url: "https://pub.dev" + source: hosted + version: "0.3.2" + flutter_secure_storage_linux: + dependency: transitive + description: + name: flutter_secure_storage_linux + sha256: a5f35ddab43cf5c8215d2feb4ce1957851f28c5c37e6f04335066a0602087bf5 + url: "https://pub.dev" + source: hosted + version: "3.0.1" + flutter_secure_storage_platform_interface: + dependency: transitive + description: + name: flutter_secure_storage_platform_interface + sha256: "8ceea1223bee3c6ac1a22dabd8feefc550e4729b3675de4b5900f55afcb435d6" + url: "https://pub.dev" + source: hosted + version: "2.0.1" + flutter_secure_storage_web: + dependency: transitive + description: + name: flutter_secure_storage_web + sha256: "073a62b3aeb866ab4ce795f960413948e51e5a42a9b0c8333b6daf5bb3208a1c" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + flutter_secure_storage_windows: + dependency: transitive + description: + name: flutter_secure_storage_windows + sha256: "3b7c8e068875dfd46719ff57c90d8c459c87f2302ed6b00ff006b3c9fcad1613" + url: "https://pub.dev" + source: hosted + version: "4.1.0" + flutter_svg: + dependency: transitive + description: + name: flutter_svg + sha256: "35882981abcbfb8c15b286f0cd690ff25bac12d95eff3e25ee207f37d4c42e7f" + url: "https://pub.dev" + source: hosted + version: "2.3.0" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_auth_2: + dependency: transitive + description: + name: flutter_web_auth_2 + sha256: "8f9303471dcd96670878c9b7c0c4e14c37595b2add67465f6a868f17a5872dfc" + url: "https://pub.dev" + source: hosted + version: "5.0.3" + flutter_web_auth_2_platform_interface: + dependency: transitive + description: + name: flutter_web_auth_2_platform_interface + sha256: ba0fbba55bffb47242025f96852ad1ffba34bc451568f56ef36e613612baffab + url: "https://pub.dev" + source: hosted + version: "5.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + fluttersdk_artisan: + dependency: "direct main" + description: + path: "../artisan" + relative: true + source: path + version: "0.0.8" + fluttersdk_dusk: + dependency: "direct main" + description: + path: "../dusk" + relative: true + source: path + version: "0.0.8" + fluttersdk_telescope: + dependency: "direct main" + description: + path: "../telescope" + relative: true + source: path + version: "0.0.4" + fluttersdk_wind: + dependency: "direct overridden" + description: + path: "../wind" + relative: true + source: path + version: "1.1.2" + fluttersdk_wind_diagnostics_contracts: + dependency: "direct overridden" + description: + path: "../wind_diagnostics_contracts" + relative: true + source: path + version: "1.0.0" + glob: + dependency: transitive + description: + name: glob + sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de + url: "https://pub.dev" + source: hosted + version: "2.1.3" + go_router: + dependency: transitive + description: + name: go_router + sha256: "5922b2861e2235a3504896f0d6fa07d84141b480cf52eecd2f42cd25585a9e8a" + url: "https://pub.dev" + source: hosted + version: "17.3.0" + google_identity_services_web: + dependency: transitive + description: + name: google_identity_services_web + sha256: "5d187c46dc59e02646e10fe82665fc3884a9b71bc1c90c2b8b749316d33ee454" + url: "https://pub.dev" + source: hosted + version: "0.3.3+1" + google_sign_in: + dependency: transitive + description: + name: google_sign_in + sha256: "521031b65853b4409b8213c0387d57edaad7e2a949ce6dea0d8b2afc9cb29763" + url: "https://pub.dev" + source: hosted + version: "7.2.0" + google_sign_in_android: + dependency: transitive + description: + name: google_sign_in_android + sha256: "5b89f1d3c095cc53dfa4f23fbfa88da06dff3fdeb1c86656f30cf8b4ca0e7af8" + url: "https://pub.dev" + source: hosted + version: "7.2.13" + google_sign_in_ios: + dependency: transitive + description: + name: google_sign_in_ios + sha256: ac1e4c1205267cb7999d1d81333fccffdfda29e853f434bbaf71525498bb6950 + url: "https://pub.dev" + source: hosted + version: "6.3.0" + google_sign_in_platform_interface: + dependency: transitive + description: + name: google_sign_in_platform_interface + sha256: "7f59208c42b415a3cca203571128d6f84f885fead2d5b53eb65a9e27f2965bb5" + url: "https://pub.dev" + source: hosted + version: "3.1.0" + google_sign_in_web: + dependency: transitive + description: + name: google_sign_in_web + sha256: d473003eeca892f96a01a64fc803378be765071cb0c265ee872c7f8683245d14 + url: "https://pub.dev" + source: hosted + version: "1.1.3" + gtk: + dependency: transitive + description: + name: gtk + sha256: "4ff85b2a16724029dd9e5bbb5a94b6918f9973f74ba571c949d2002801879cf5" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + hooks: + dependency: transitive + description: + name: hooks + sha256: "9a62a50b50b769a737bc0a8ff381f333529df3ab746b2f6b02e83760231455ba" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + http: + dependency: transitive + description: + name: http + sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" + url: "https://pub.dev" + source: hosted + version: "1.6.0" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + url: "https://pub.dev" + source: hosted + version: "4.1.2" + image: + dependency: transitive + description: + name: image + sha256: f9881ff4998044947ec38d098bc7c8316ae1186fa786eddffdb867b9bc94dfce + url: "https://pub.dev" + source: hosted + version: "4.8.0" + image_picker: + dependency: transitive + description: + name: image_picker + sha256: "91c025426c2881c551100bce834e201c835a170151545f58d17da5180ca7d9ac" + url: "https://pub.dev" + source: hosted + version: "1.2.2" + image_picker_android: + dependency: transitive + description: + name: image_picker_android + sha256: "6f3a1995eafb000333174fae92202622033b0ee7fd917a6cd3730295264df84a" + url: "https://pub.dev" + source: hosted + version: "0.8.13+19" + image_picker_for_web: + dependency: transitive + description: + name: image_picker_for_web + sha256: "66257a3191ab360d23a55c8241c91a6e329d31e94efa7be9cf7a212e65850214" + url: "https://pub.dev" + source: hosted + version: "3.1.1" + image_picker_ios: + dependency: transitive + description: + name: image_picker_ios + sha256: b9c4a438a9ff4f60808c9cf0039b93a42bb6c2211ef6ebb647394b2b3fa84588 + url: "https://pub.dev" + source: hosted + version: "0.8.13+6" + image_picker_linux: + dependency: transitive + description: + name: image_picker_linux + sha256: "1f81c5f2046b9ab724f85523e4af65be1d47b038160a8c8deed909762c308ed4" + url: "https://pub.dev" + source: hosted + version: "0.2.2" + image_picker_macos: + dependency: transitive + description: + name: image_picker_macos + sha256: "86f0f15a309de7e1a552c12df9ce5b59fe927e71385329355aec4776c6a8ec91" + url: "https://pub.dev" + source: hosted + version: "0.2.2+1" + image_picker_platform_interface: + dependency: transitive + description: + name: image_picker_platform_interface + sha256: "567e056716333a1647c64bb6bd873cff7622233a5c3f694be28a583d4715690c" + url: "https://pub.dev" + source: hosted + version: "2.11.1" + image_picker_windows: + dependency: transitive + description: + name: image_picker_windows + sha256: d248c86554a72b5495a31c56f060cf73a41c7ff541689327b1a7dbccc33adfae + url: "https://pub.dev" + source: hosted + version: "0.2.2" + intl: + dependency: transitive + description: + name: intl + sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + url: "https://pub.dev" + source: hosted + version: "0.20.2" + jiffy: + dependency: transitive + description: + name: jiffy + sha256: cabced5ddea4612f5e0c498df065ee8a1bd810f7ed757db910e95d2aba80161d + url: "https://pub.dev" + source: hosted + version: "6.4.5" + jni: + dependency: transitive + description: + name: jni + sha256: c2230682d5bc2362c1c9e8d3c7f406d9cbba23ab3f2e203a025dd47e0fb2e68f + url: "https://pub.dev" + source: hosted + version: "1.0.0" + jni_flutter: + dependency: transitive + description: + name: jni_flutter + sha256: "8b59e590786050b1cd866677dddaf76b1ade5e7bc751abe04b86e84d379d3ba6" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + js: + dependency: transitive + description: + name: js + sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc" + url: "https://pub.dev" + source: hosted + version: "0.7.2" + json_rpc_2: + dependency: transitive + description: + name: json_rpc_2 + sha256: "82dfd37d3b2e5030ae4729e1d7f5538cbc45eb1c73d618b9272931facac3bec1" + url: "https://pub.dev" + source: hosted + version: "4.1.0" + 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" + logger: + dependency: transitive + description: + name: logger + sha256: "25aee487596a6257655a1e091ec2ae66bc30e7af663592cc3a27e6591e05035c" + url: "https://pub.dev" + source: hosted + version: "2.7.0" + logging: + dependency: transitive + description: + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.dev" + source: hosted + version: "1.3.0" + magic: + dependency: "direct main" + description: + path: "../magic" + relative: true + source: path + version: "0.0.3" + magic_deeplink: + dependency: "direct main" + description: + path: "../magic_deeplink" + relative: true + source: path + version: "0.0.1" + magic_devtools: + dependency: "direct main" + description: + path: "../magic_devtools" + relative: true + source: path + version: "0.0.1" + magic_notifications: + dependency: "direct main" + description: + path: "../magic_notifications" + relative: true + source: path + version: "0.0.1" + magic_social_auth: + dependency: "direct main" + description: + path: "../magic_social_auth" + relative: true + source: path + version: "0.0.1" + magic_starter: + dependency: "direct main" + description: + path: "../magic_starter" + relative: true + source: path + version: "0.0.1-alpha.15" + 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: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349" + url: "https://pub.dev" + source: hosted + version: "1.18.0" + mime: + dependency: transitive + description: + name: mime + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + more: + dependency: transitive + description: + name: more + sha256: e252628d2183cc09539b686abfbd9d8302675959b89a2a8146f5f4baca6ac5ba + url: "https://pub.dev" + source: hosted + version: "4.7.0" + native_toolchain_c: + dependency: transitive + description: + name: native_toolchain_c + sha256: f59351d28f49520cd3a74eb1f41c5f19ae15e53c65a3231d14af672e46510a96 + url: "https://pub.dev" + source: hosted + version: "0.19.1" + objective_c: + dependency: transitive + description: + name: objective_c + sha256: "6cb691c686fa2838c6deb34980d426145c2a5d537491cb83d463c33cdbc726ed" + url: "https://pub.dev" + source: hosted + version: "9.4.1" + onesignal_flutter: + dependency: transitive + description: + name: onesignal_flutter + sha256: b0910a2585ca70f2ffa370c0878625781807fd301e89005868fbedb1e933dbbd + url: "https://pub.dev" + source: hosted + version: "5.6.3" + 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_parsing: + dependency: transitive + description: + name: path_parsing + sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + path_provider: + dependency: transitive + description: + name: path_provider + sha256: a7f4874f987173da295a61c181b8ee71dab59b332a486b391babf26a1b884825 + url: "https://pub.dev" + source: hosted + version: "2.1.6" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: "69cbd515a62b94d32a7944f086b2f82b4ac40a1d45bebfc00813a430ab2dabcd" + url: "https://pub.dev" + source: hosted + version: "2.3.1" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "2a376b7d6392d80cd3705782d2caa734ca4727776db0b6ec36ef3f1855197699" + url: "https://pub.dev" + source: hosted + version: "2.6.0" + 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" + petitparser: + dependency: transitive + description: + name: petitparser + sha256: "91bd59303e9f769f108f8df05e371341b15d59e995e6806aefab827b58336675" + url: "https://pub.dev" + source: hosted + version: "7.0.2" + 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" + pointycastle: + dependency: transitive + description: + name: pointycastle + sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe" + url: "https://pub.dev" + source: hosted + version: "3.9.1" + posix: + dependency: transitive + description: + name: posix + sha256: "185ef7606574f789b40f289c233efa52e96dead518aed988e040a10737febb07" + url: "https://pub.dev" + source: hosted + version: "6.5.0" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + quiver: + dependency: transitive + description: + name: quiver + sha256: ea0b925899e64ecdfbf9c7becb60d5b50e706ade44a85b2363be2a22d88117d2 + url: "https://pub.dev" + source: hosted + version: "3.2.2" + record_use: + dependency: transitive + description: + name: record_use + sha256: "2551bd8eecfe95d14ae75f6021ad0248be5c27f138c2ec12fcb52b500b3ba1ed" + url: "https://pub.dev" + source: hosted + version: "0.6.0" + share_plus: + dependency: transitive + description: + name: share_plus + sha256: "223873d106614442ea6f20db5a038685cc5b32a2fba81cdecaefbbae0523f7fa" + url: "https://pub.dev" + source: hosted + version: "12.0.2" + share_plus_platform_interface: + dependency: transitive + description: + name: share_plus_platform_interface + sha256: "88023e53a13429bd65d8e85e11a9b484f49d4c190abbd96c7932b74d6927cc9a" + url: "https://pub.dev" + source: hosted + version: "6.1.0" + shared_preferences: + dependency: transitive + 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: "93ae5884a9df5d3bb696825bceb3a17590754548b5d740eba51500afc8d088f5" + url: "https://pub.dev" + source: hosted + version: "2.4.26" + 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" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + source_span: + dependency: transitive + description: + name: source_span + sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab" + url: "https://pub.dev" + source: hosted + version: "1.10.2" + sqlite3: + dependency: transitive + description: + name: sqlite3 + sha256: "37356bcb56ce0d9404d602c41e4bdb7765e7e9732a3e47adb3d98c556a6abdad" + url: "https://pub.dev" + source: hosted + version: "3.3.3" + sqlite3_flutter_libs: + dependency: transitive + description: + name: sqlite3_flutter_libs + sha256: "3ed7553eee7bb368f8950f58ba29f634e06e813c029aff6a0d60862b96de8454" + url: "https://pub.dev" + source: hosted + version: "0.6.0+eol" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.dev" + source: hosted + version: "1.12.1" + 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_api: + dependency: transitive + description: + name: test_api + sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e" + url: "https://pub.dev" + source: hosted + version: "0.7.11" + timezone: + dependency: transitive + description: + name: timezone + sha256: "784a5e34d2eb62e1326f24d6f600aaaee452eb8ca8ef2f384a59244e292d158b" + url: "https://pub.dev" + source: hosted + version: "0.11.0" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + url_launcher: + dependency: transitive + description: + name: url_launcher + sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8 + url: "https://pub.dev" + source: hosted + version: "6.3.2" + url_launcher_android: + dependency: transitive + description: + name: url_launcher_android + sha256: b413d49b73867ac08dd2f9890efd3cc11f2a0e577618d50843440a1fb3776c32 + url: "https://pub.dev" + source: hosted + version: "6.3.32" + url_launcher_ios: + dependency: transitive + description: + name: url_launcher_ios + sha256: "580fe5dfb51671ae38191d316e027f6b76272b026370708c2d898799750a02b0" + url: "https://pub.dev" + source: hosted + version: "6.4.1" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + sha256: d5e14138b3bc193a0f63c10a53c94b91d399df0512b1f29b94a043db7482384a + url: "https://pub.dev" + source: hosted + version: "3.2.2" + url_launcher_macos: + dependency: transitive + description: + name: url_launcher_macos + sha256: "368adf46f71ad3c21b8f06614adb38346f193f3a59ba8fe9a2fd74133070ba18" + url: "https://pub.dev" + source: hosted + version: "3.2.5" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + sha256: "85c81589622fbc87c1c683aaea164d3604a7777495a79d91e39ffcdec39ddb34" + url: "https://pub.dev" + source: hosted + version: "2.4.3" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + sha256: "712c70ab1b99744ff066053cbe3e80c73332b38d46e5e945c98689b2e66fc15f" + url: "https://pub.dev" + source: hosted + version: "3.1.5" + uuid: + dependency: transitive + description: + name: uuid + sha256: "1fef9e8e11e2991bb773070d4656b7bd5d850967a2456cfc83cf47925ba79489" + url: "https://pub.dev" + source: hosted + version: "4.5.3" + vector_graphics: + dependency: transitive + description: + name: vector_graphics + sha256: "2306c03da2ba81724afeb589c351ebbc0aa7d86005925be8f8735856dbe5e42d" + url: "https://pub.dev" + source: hosted + version: "1.2.2" + vector_graphics_codec: + dependency: transitive + description: + name: vector_graphics_codec + sha256: "99fd9fbd34d9f9a32efd7b6a6aae14125d8237b10403b422a6a6dfeac2806146" + url: "https://pub.dev" + source: hosted + version: "1.1.13" + vector_graphics_compiler: + dependency: transitive + description: + name: vector_graphics_compiler + sha256: "142a9146f447d15b10bdc00e21d5f4d83e5b32bb5f8f8f5a04c75311344923a3" + url: "https://pub.dev" + source: hosted + version: "1.2.6" + 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" + 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" + win32: + dependency: transitive + description: + name: win32 + sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e + url: "https://pub.dev" + source: hosted + version: "5.15.0" + window_to_front: + dependency: transitive + description: + name: window_to_front + sha256: "14fad8984db4415e2eeb30b04bb77140b180e260d6cb66b26de126a8657a9241" + url: "https://pub.dev" + source: hosted + version: "0.0.4" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + xml: + dependency: transitive + description: + name: xml + sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025" + url: "https://pub.dev" + source: hosted + version: "6.6.1" + yaml: + dependency: transitive + description: + name: yaml + sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce + url: "https://pub.dev" + source: hosted + version: "3.1.3" + yaml_edit: + dependency: transitive + description: + name: yaml_edit + sha256: "07c9e63ba42519745182b88ca12264a7ba2484d8239958778dfe4d44fe760488" + url: "https://pub.dev" + source: hosted + version: "2.2.4" +sdks: + dart: ">=3.12.2 <4.0.0" + flutter: ">=3.44.0" diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 0000000..ec43854 --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,135 @@ +name: magic_example +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.12.2 + +# 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 + + # Local path links to the magic ecosystem under development in this workspace. + magic: + path: ../magic + magic_deeplink: + path: ../magic_deeplink + magic_notifications: + path: ../magic_notifications + magic_social_auth: + path: ../magic_social_auth + magic_starter: + path: ../magic_starter + + # Dev-tooling, imported by lib/main.dart under kDebugMode (release tree-shakes). + magic_devtools: + path: ../magic_devtools + fluttersdk_dusk: + path: ../dusk + fluttersdk_telescope: + path: ../telescope + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.8 + fluttersdk_artisan: any + +# Force every reference (root and transitive) to the local workspace source so +# the plugins' own hosted magic constraints do not clash with the path links above. +dependency_overrides: + magic: + path: ../magic + magic_deeplink: + path: ../magic_deeplink + magic_notifications: + path: ../magic_notifications + magic_social_auth: + path: ../magic_social_auth + magic_starter: + path: ../magic_starter + magic_devtools: + path: ../magic_devtools + fluttersdk_dusk: + path: ../dusk + fluttersdk_telescope: + path: ../telescope + # Pin to the stable line: magic's ">=11.0.2 <13.0.0" otherwise lets the + # 12.0.0-beta prerelease resolve, whose non-null saveFile params break magic. + file_picker: ^11.0.2 + +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 + +# 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: + + assets: + - assets/lang/en.json + # 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 + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/to/resolution-aware-images + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/to/asset-from-package + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/to/font-from-package diff --git a/test/widget_test.dart b/test/widget_test.dart new file mode 100644 index 0000000..0b2bbdd --- /dev/null +++ b/test/widget_test.dart @@ -0,0 +1,10 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:magic/magic.dart'; + +void main() { + testWidgets('Magic app boots smoke test', (WidgetTester tester) async { + await tester.pumpWidget(const MagicApplication(title: 'Test App')); + + expect(find.byType(MagicApplication), 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 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 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 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 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..16a4584 --- /dev/null +++ b/web/index.html @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + magic_example + + + + + + + diff --git a/web/manifest.json b/web/manifest.json new file mode 100644 index 0000000..0471dad --- /dev/null +++ b/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "magic_example", + "short_name": "magic_example", + "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/web/sqlite3.wasm b/web/sqlite3.wasm new file mode 100644 index 0000000000000000000000000000000000000000..fbd7d8d7b6f2ce3c82eff971061ad0471ea70355 GIT binary patch literal 744124 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�QYlHM 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

1Mk4Lf7OUo+k# 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 }KA zY}%AS@h4T-AFZNp+1~ph2L5k*NnJt5=yZ6DdAA zI&~hbn2hZsk-UkelF||Bgi!*iJ0_xzBgAC1C9*$tUyL<-qk_nLra5>i#a8uTGbOvY-%8`guCql3o&upmWj{PFfHbcR=4CP;vb5%mUXe&e^HRwgFaT| zd7eSeJlU%HY)LycKpc(7C^<=WS#%CR=rrGz0%%(VZ4dh(XNP2sl13SSJ(dhxUIj8z zij)@42QRhM744vx!bZdB=236HdxF8qwPHU2UIi{PP<$w|nT^efyfFc{WHT(^mbIu0 z?|+V`)oz8oFJG5ss+OJRQJ4m!r;QjL{&eofThVck)} zRcf&W6)i*{(8MFI0MF4!l+?C#nnp4D6;A=vhPl+B}&qijbK}~Lp!`F zR+UlK@->(v;Vf{sph=OAh87V7(mb_}6M+}UzASoN77rqQSq2eDx7ZZx9phlnJvtlQ zCLl$aVgaNuE|Ad~cQ|_pH0a!Y-?L0f>dT2ineISXz}GOff1bu`2EDLuMk4F=9Rr zgLZ2uhRu+eTScfSeOn$mlqssyJuM5?!{VVgS#~cUqy#j+qW*hPLT8znOjbp_ZN zvuO2;vbr*^G+FlzM$&>HQI%}nDI%O>0cgvl5IZpakNV$VAxS$Fq(E)q!Xz=1BKm?B zAd#Cs0V>`wJ`jRr)G1ogAaSzBgsE9K8Jb==dz~s`8wj=GFa$7IW>AYj-}}48^0Z5f zP~UQ-sU~M2JcC*{bZ1$L(|EKJY$;WSPcx7TTZp7X4`45{7)DHWRi)BIa8$NjIBcDS zA%8H|`UBnOJZ>ri$)%D$M`-Ih$VrLho<1O3X5I?a5I7F^2*?AM^hI=+viaW_*c zx?I?pKGviULJahpSO;p(G3iJKu2sXS_1Pm*;yrJzOcOvQf(V(w@F?M>{|6JKAAT|d z)u>nPJlesEtovB=*es9|F9K5$32o48MRkLkzzQ!c+06^4L+nn(z2uMNz*8a>8Ju{z z+%|Xsn4-UpH(Q@9LW^hBJbmO7Z$jEuKf)a+Ye=E$C5)J>t@;R@_1WY&SOGF~QMmZC zL@zL=#TxX^m6QWZVV9Rr5sDnhbOj!RQ*kt!h4YnKZ4Q)?5USRC0~Or*GC7Yr^3zH9 zK(cg-vuXU+>~T8-h)WkX#CRJ^n-WRJ=hzl?;3}NeMbkMuHpV$NMJ6Q#>&f6UX*~Lc z$&%}x1(uu^$DT8g8MJR|PcbSsx?yITu)s5qcbMV)B-tYyOwVPOm*L6js7k~oVk2qF z(@Z&3x3sf;xDu_M+ATN}g_UDLCy30}n0KVZ)GJTOV-Gc9?>q`a36#2ZpnlYvt_%BC z5td6Z8vx)R89#6pMW~Ls93KG#h31h|_<$*iZX~8#A5$ND1$zcl97NqG27FBYpeyHg z`^G`2Vg(g?2RV$eWX+8t2jmp^vBcrMBBVNS(c#StYd~Ni5(Q)viv{fs1;JvIx1q&S z_co1JSCI-A=!Oqp>QFyC6z5tWM?W3%lwCpra!MZO6tp}n zqEw61H9)|0`2g(nUoS%^f@ow#;9d$8^c6|GjZ-dRhNu|qz9Whe{)u|B zyP3C1Bn*v=Wl(H}3b;Ni{3`wdCkl9{PXL>Cd9i45t$QK{b26j!^6+dpZm@U_CNfET z%bFklkuDSq!zwk&BYgxgS`cA;r;qp#Hj5zCf^P1fW09B0r5l4TGhq0D~U(nu#5Th+qwo!c27cGbk-FMDz5Vmap;k0!FDH8+72;Le1q) z7epb8O};+)tVV3kfKsoO4h-9{_7hy^$}0tEm}VnAQ{V`?cT2IOmzM@YLG1_=qqn}1=& zz0y|ik>8sSA=_WVQ~0^Cye6Ou)0FxxOf7aG__7GfDBohuObi*4Z?oaBnWWi`N8NgK zbH~E7x=N>AFV&(*!D66Jh2fF+qSGQg4Ce5#->D&Ai#}P+3pcU0y7s4==Mlm<7(%9& z&_Hk4SMO6FrWGH*-U8Zj;Slu;SZnBx#mV8{3I&Rm{LJVWFnQH6W=@tpISn#6q2n}JX zo=j>b-ZRKiC3}7L>jq@iOSm6E#sjy38GRvZuKm1BtEFj@wM<-0t+*^?s4+4ua!d#I zB&|eGh(ljXzF>o4L4;OrCOeFV*LjAhDr?fCH=&3f0`QIRpfbQUw9DY_$vji`B}a2& z9PYD9dAPKIxC@ceS1rIE+fL@G-~7mJo3r8{c;NY-O0SS5VTCs^fWKuJ{M%Tethb_k zWt+ak)t9oCT;oopA(UXc0Mf=t1?qWA>yI@^&IbXAy3I&%Wsi~kA!vp`w5|VX9^^_7 z4cbD@3gVNq&MhVtZ@EX=pS1d?dC>}N9$NztkyYGjTasLGnMVSVYk*?~z*}n`1_`}4 zw-Y7|w`nIap>BhuCi71%hC!lE!WQ`-p)`ex3Y%a*bATMq1C}Har8!IjFGrcuHOi9s z*GjAw7IG#yj9Pq4T9i?wpOpU1xhWhlEWyB6S(fG+0NOH^HkcjxoriIPHZw|%mbX40 zsL}G3IItHgKnbe-*pyj!lpNI?#HbWZ`urHD%OLWQ4GUW$H7wh?7qm5Nzvo<;OA5AY(3cABm#>zs-Y$p;sO4QS`BY11bDdoY$ zQnu?REmu6smlg{=U}}K}+y@URQjT$mfYI*Pwrj%xy7S$5*#?OV*=S6CH z%Fn}Um0d%l9Ea8g-fFiio1*&;D%9b-P5-J0d(Z~qB4|WhiuGb(qbGo=2m+q(!xh~t z%aZLuEfP_+JvtP+EC3oNZHXi4y3U9mISzfPFYyMWt2{LI%+OF?d$w!MjmSq zDwS-OVwK3Oj}R?jcXxAmv}RT5nphtg56|$P8<8ikqx^9%^wr zl_khPYZut$NZn(*X@&>cCG_gZl^TyNXvL9G)4ea}^p?^2x1EvNYo;VqX-Q=)5tUFI zX4`8(L}VVoCDP;3f~^w)R7`B6;nk-o{=tk{S1j06PPW)mQ=(^bI622cr+;?;x*LZf zjkjNIT-|gKY$K^bpt@MW<&N=^?w=P)eqM_! zEC--L+P`Uwj}OE|D4OLOO%_q^9uP;&nIY7=1)5|fY)5~AWmZ3(uS zQbBCCkVOS8NXaNNJxIF_Qo{&ZMLR4|b*Eg9H6!7{4`f^Q$;ht?{{&Ardvb`K4IeB< z^=BnfG{Dfey;IZ2$>{@$xd6~ec7|;Sa9gIXvKNaT zI?Ai^Awh&~XSrO$LCoPTDYTlfu_)DIVa zPx^bZ{Vfx4JmdT4I#D1ht)NRUzqsRY!{_ zYUwT=%5-%oB*veUw^gr7r7cQIe>nx~Q?Try$;o(RTt2LdJ-Is_1K!7%^>8|IkdBqw zeR5;-BKCvDNlX3$RsV1YBX1uoE=Z-NKc}K=!8E{ZQzYPhubvXf zv8upd^HS2Mwv#SEA1{-6GSSR5TAKg%ee}Pyc{LY_yQ6)aUcClW!#?urRRBxC+{~w7Th(Z}xjT+?*SA$4!@f*7Bc8yfh_zlwzL(>PZ3Iw2 zC*1gx(zacwt^!JFJU)=p)SLjsdg2HoHP@^S5ue|A6HDeOyU)|{rvBshMaLb6zfq)C`qlvjfV#-Hg zE1i_ge*JXOU*IZ~)?>UcVL=m@82yd8;kida19HvVUcxe48wz&NIVV+Z9B; zeh=DO)zH;A^mKr;De?$|3~pvj(sDAc!w0b(0c(9FqM z<->BF;p_)~>Sp7n+i+9}{?>=3F~yXvp!SREfWHVx@B_uj$z#}a6^u;J9%Qg0E>;I! zY)VtvDt05zdj|8=j+#<(%XiS);+5swz}1~#EIp&CGQ@UR?D=3k41>D1+P#m7?D^&~ z?3^7}W@4<#6~oZh|6rE%Ly6A@X2JkTx1N){E+{8Zmq>g=wj)X(y>o;oPmf#kAZlFTk zy!|IR+2{P_hDc{&ng2HEH8GU`X9Y>Txj2a3HR^W>w9NUZ zC~ePa8CJEar-R^J@!>R=7T4T;JNb4lvAnE6OL?(^wgDv&X%MaonzWcq#)Dd1P|X&$ zKqqvbXdZJ00lz>D-`xqG`K*-He$WUW8;=hZ5$f?p)Ij>jL-vf45R9TFeOGb`$w%Ua zP#FKov|(n-NVh08-F$TS4k4rm@76rj-57TcW5O$@+9Aol>q7z;r6YjL=wKDeWH3wv zX1)eb+CU90o#xB|5nR_hxOqGLaJ>8XN2eUI`?4rLN~7*oX<4KvE@m)-zj{wmJyyj? zc*A~h9GvNalaVeJx+a@xb`F+{k7ErPT+rskMR;MyoAHb44tE^8gM-7N3f-TlZC)(4 zh!vhzG*9VE&#Gva(4qaSZ4uT5tv~Slgtj|7QMFk_iq}#P9Zv;X#Exe2qSeF>w5wT1 zkh%w(-LYjy+R~1qLw5x2LJ0Vc*Xr@JRv+Ljag0}PZpu)EwnRntWguF(+!SwPu8wo- zBn|*bXg}f+Gb_e((3Ao3KWYIo43&se7_TE@fmsRBsRIxe0sSx*LNf%-BU}OQ&T_t4 z^;pG;0ya!oL@g0}I9$`)o@7g7$`~(khrJ1*>xUN}0=0}V18YSO8b7ZvQijSn&R$dC5=S6$Q3PJ{ zqCZEg*CF}iDi43Bf$+gfcC;ye`;hCwAZ?K?P%>O9Ke*UOkGL}~6)dFFk)?qRqi-$K zBlL7(^UxPIFYoQ3NrGJ+j*S{^S&ofX+f0SB4oZ*XV;*0Vbq6FiurmvblAw8b<$;}o86|mdvF$)z?FVCV8Ta|vy4pKtxKHe$ zyg+{80omJy2X^h+HFwM|S0-f$(-j-uk=J_RX=MoEGedrS&xO5LRGo-bN9 zJE2m_uK@*Lc|T5ebyFkQfD*r9MNVA|xynF^RrjRa>Sp-JY;hgOPt9j%$2byg#F{vi z=9^#3tJe}o>hn$vHAdKc0E~IXDZ!?ltnn(x11 zTiB1TQdS}At9391;uudbQ%%|5-y)&LO^XOVnHQyUk7piir@Cm%rfimN6qo9m>RL^? zGj}gvD=R89)9MvtYN~=>AON-u#|dNClIQ>g9Y?N3JaA85y;!#cC@&s1mz-Gajm6rO z0mT}wesubfXjX2@*wt6}Hf4wh)w&&9hCCs{KCF5@3hCVLI3jYK^P*14v0{X;V1qYw zn8tLyBGn2(SZ>NNK8-z{SbP~aoiYN(o8y~ba@^~f`rVF$T{jy}Dz8>jvPa&NPib#) zVyPwvH}7R5iU!N%ifNhpHNns)`pgL@Lt>cABe`2}U>2TOpxU@$>cnE6FAI>;{mxN| z+?r?OJq^yT=m0SVfl9-eX1H=Lm@o=s2%CD$qJCD}B7jWB?D~v@hA#Aza&?yHQ&Nr* zk%)L;hAK#&Du_r=ms(};fy;M2Aq+9WbLu8)2ii7S52>@0 z{B@##9_^ovJ9kp{;Y`Z;rQ477;+%gP|7;%J<}A37gb5{attin?=N%?HQ)ieKvn>yF z6bMCNz*(|>3v(SCTyY07iBNc75PXBYWOFErDqGDgO257-(`~e1Y zfmOi^6IvCjN@^ISSfg}m#jmzAxr3qg5m0so+`uWe@XZf^(~${n44KH3_w3nS-8q?h z5;hAsgyzMQWE$%Nkm>@5lp_cZ5F{Fv=}}=2A{ZNqdkk=w-nCZxtLd><099{-Y7;us zL47HiB$pUixJG#+O#F>Q)XOHDyUT6agS;-9?{Mp6{Um7<{Ng<%baehToD6tq5!YK| zP6!W(Y(9<`CPQgc_IPe~kIw2ms!2K%tak{#dlX}rP^vdh5e{&y-3N0Cq5anBp-MyR zgw3kYO5 z5UHv$?u|_u!kiXrDc?lRpR!+KQ+7=E5wW^Xb8;BUlLK${RrfL$XT?nB$0*jiwXOZp zkreiqsnSwggYf2(X{6G`Hg=PK&wNE@>DkzrcXJ64Ihc;EJvU=AmEAK~G!}4>mEcwZ zJTc+dXV4|ziiK~Hf8#Z7g0Z#u4HQnIwM2RJ^<;)o?$88c^m;`ZyhFC- z(!o3#Uf|TvlTVa#zStQh>lPqf3XasW#I|wYWgsN8ZpyYYkYt|68A42#Yc@ZvdoK0+ zKVG}EU!`*=vhM+C6mY%xa+Do(A)BXN{@Y+~fyb@k0W= zzXLWP3+wdL~a9#4)Mokt^b})3l?gWGX!-d=}>m> z?xZT^luaI!`~ZX)3_-BygrrG7i;|~!VB@ZJ^jKbj6CPw3D?WLnw41qB0G)jzuh0hj27r>z{M3~6Z*z%dp_~_R z;+l@W>K!a12$jYnU@#7ZMQY5bq2nQuNH-WLLOY2Nu`l%U<%UA_ii@%^FXl}mD=*1l zt9MSNu{HBALoLL5k+`Z$^W#*h84QSrQ-c}$cKaJK1sn|pF`9}tQ(AWQs)=?#G?V_` zi=A{V1kbt@fN))cpraRX;53;bd=G%06TuDItiEBj%SDJr)>pXzRhYl)+(K38(Mtf) z%Pv1G1&s(~D!U)-#tCb&h~T(L%LnD<1A?p|N&XQXF9yAoYCA0wYJM?u$Au0hNjhE~ zD^6TjRL552`%%UklF7IeRFw8z;mltaQ{&WtRhDa{78t4=Z+Zd66Z<0)=O@wJidZlj z(BR8-Wc7hAT5=P6STJNQjaQO}v~7^Vi(^=!saafnz@mqKGgJ%c207aXu>?pG#y1cF z7(a7vt}3EGCk`|H^c0$x3)v3ppVByeYeTS0MpGrzpwtBDmz#k!LkK|dR0NiUP4yX@ zB5vN`TTO`j7D_34g}Y|L70hp%H-==Tp9}%6*;ai8MSvNP&zhw{^zN=2BsJj{!v z2h_vmb6SX(&*gQtnrwVt;La?bW=7)DA2*QS-uGG}m*K_6vN@_Ltb(29H8O6vesseQ zZ=_dq%(mtmd&<`B&5f{m_)Inr0%Utl2*51c&N(~zytFubCodxNSe4DbIF2W%jdt5Q>@^jWtB2YZ{9xojqw9&YHTA@WE*CYK5Y9b`7gw`u?w3$rMo*(>l}s1Im#W3;PmUaN_g$2rzvV*doswWG37C zS4&d1&k9Nr(8tM%9?sxRew|f)4fqkX_f9+r$5?ftyn(IE+U0zmyeNM0ZPi6Pcb=H7 zal!KD%8mhCtbY(UfJ!sUMb7hfjUeM z)A|ZC!&GohS@c-bG4&A!dv96opK@u^p2pf;cUa+5oh`B)mwb&y2HxcBqR^ zpEc|KWIp&}c~OMOJ2Bz0dti}&+3CKnSWr5-iv(Xc#1dJ9il=pyY=KPjFKL?a#2SuAu#Mhvtrzze2l)iPj1y}7jWVSSWB3!8#@ zG_T-psEwY3fs&U9k3oDFVG*`6N@T#&dLkvbd(h|$ix?*{R8(J2o_%7s0oc&zQj!-I z_VJ@-i1l$1K=SGnQopOf_fI9N>%KC#>Llt;II__hVs=BFEDQSOB!qFU3!(bg<_$>C zc`|9@wz>!R*%u-#nqGi3&|{^=1OsG+JZlI(pNmo}WmX@DHe#tjCGC~uF6mxd6lIFk zlXA>18{5XN#@4j~7y{J>(4oO#ez&b_agWhxPMVD7kNCKbk8%B(s&W(^-T#)-A;^Y0 z&38Twt4rwwgBT!G2kJF03LVl*4Hsmcy3MyQooc0plotvXplsfJfMMfSRN4B+JCw4K z?-Qs{Y*F&8Pp;?~4BAR`z{7h#LD!fMlBBap9e105Sm>wQbd@1=hP6jV)UzUQuV$^l z3L4guW(1qsgC+6a6?22^;~aTQ_kM!2}dF0rP9J<|pC3nP~2Dmo>_cWinU^t>jpcD?(ppP!r@V zomgA}in9#t!J)E+N%R2nHITRGh-}KfAFxFu(=hObHWB3O;4#vh{1xIA0px(as2azR z!Jju}>*)k=aax&$7JD>Y?^Vechl^QO}#w>r^0L#3*$Th6hf< zAn=q_t`xpZ508}R01S_O{^Lj%Dpea6Ho!{87~%-$YjW?=!PQZyqx!@_=U^9U1E@R} zmPv@ocyw4GSjDr&uE!xMS&6#v__xT|6C|*Rw{|RZOP{j=&+Q4WFbVjAnxyxNxG1dR zCG5%$#@;x65w-C>`Dg;odl2njtA~M>sZ|^P@K`{QW3 zJ4Dsdvav`rjA7!0>`~1F|6vu6N5!b{^1@*tITDO8oEez{7Zd?3`-^RQ zMCfuqlp0o1VnMX7+ay?#G|scsw46aulk%cP9Ni`JS*8l4&^}rAwBD300GNRsL2EBK zsoczLz~9+oZ=s4schwJLsj}v%=1;!`g9Uv+=cB3@rY*&`Fbv;o^*b^j;?1+QJ3+c8f%Q;5&*7zGFuE@+XYdLPjo=vymn zvimHAgyfe*m$1gA*L*XRw^TP<{)KAq-~9AqwfE>!=F4oH>So>vHoO;blW`fUqSw24 zt3ng({vYx9=kAz zqoCUk?8k|t2|Qbd>H}wSPy~pCZigsj9gM4k0m*|0G+C(B&FQQ=pvCud-f)6*T*9;a zXkv9b(Lv2r*9;0|5+}R7dG$S*G;P5NZH8|oW5&0Db;dY#Kp1E&kR`C#05$~+Xt2AI zsWQLTqVw z)%-r%0cJxs0GFE!{uQ8q3Ik~{Otbb<@+pvYU1n@?2;zkemm+%>$le&X&elQwkiOxC z>H{c*5wo+-Eyg&Hj>`}BX)b7EJp&J7EKkrflbbE>01m7M!y%*Xn`LYZ`4BBfBq;ba zabETzyaw#hUueEHp*i2UU6CJAv$#+0XOLptn@{)VMRH9EHB5(6yR=z#OLC$8DIEpC z1wsOZJvF@@q4F`j-yBY%8@Kxf2L#Rs;7iky`1?B72<_6v@@CQl)Oda-sZ)K_Dd7Vv z0iVdyk+$2W^>P_%T~NYr|V_vc>&59xHK~aHI5Be-t-V0hz%3Q1{jM2N<(y5sn{B-4janb zq45wICanxqRUNDVPeP8aU`Fhn8@( zG)tJ3o$k&2y;y5HP^g^B8w&t`Fu5_Hm#$|PSzx!|XI7r1-cAbk2rhp_w+?X4w(_hM zb_wZ_@T%KN{)A3d>x$T?@XwmC(X;0MD+&a`ZJ5&u^87j_CsUly6N~Th8RBHm{9kc*skqr z;_`N|&?F?_Fyl`^6GK25NPVO+u+5@v01nYU7yaxRJ(oJ#GkUIh{o9J>&pt1FSu@Cj zF4FKIvPkeq&Hwr5ZD4PD&FltIEf^UDC5M3O1Ft}{$H(}@BZ7y%_SLLeC)s%nB3pM; zi*LHdZ@BpxFugA&%|*<(^-qh08E{s+l#El?_Ss?x#q#lm`~${73!k4 zK1ZlhOwpA^3-~KI5NhZTuu_DU;-S51aZJyB8%c*|QgeKHJ+_hX%h$-xjNl=^CeyF` z2_^EY$1lL7MzvfOd+|x=D^Pgv>&rO;%CL(caxn1}+u!5GwoxE#Gi{@Hq+d45ldmbb z_o%!`zvj__f0#G-;gW^vU4t2;YTc2*!AC`bG&_O}9ls%9Wrj}@EKztL!t zY6wZzXuYOR#;V>0y=Ew5YT9U_GJ873*fAhOJF)KgH?V>3BGygvoJnkGaOh#_YsM?1 ztBQ1R)9%1Ae2*{-rXLqj#QCP4gz%3mcOZ%s;aT4=V~?t)DE#zAs0gQ@Wh(t%Rxh-Q?Tq?Esq~uR5{3-xs&aeMiBy!cmku6 zj7uDc_;&70z8$kqB*ts=Cx}FnGKBk7yX0_On&nK}G)cGuH|kD=2&%83=?aC?>=f3Z z`Va;mHVyOn@RUB!bPY}$S3C=6@0F3-(0xsTA%#MJC%FpASrt~c_OomRd;>51bTp0* z^w34JRDp?#Ml^Ec0zvgDf#R{FNqF_xEWYN_q_$1mGeu=&5|CDyDdC9_ILwqb07nk z$z@1|ucj0R<5B!NDn#qZ!i$$N{Gsnc-ofobEY6!G(cv#AsCDTt%d|0bo@wech`Pmd z`u73F2Y2HWjG0Cg(IJu66mV|-?N&fkeVj~|=Ca$;S62Z+O26ec&T1Vx<6sH@abgLXB_d+tfQ55-oNHnU_X$fpygp62vc;yhw2`hOYJ4 zwN?;;ACgen%aNVI_kYEo!p8^LP|j%cvF2Tu71evD0CD%>3Hb=r=MYe?V=lv#{AZX3 zg@v3zZ2Y}Wx?-%}<{BMTugF(xupHA|bM@4M*=b~e-D1UNM@(~wOE@qV8xikFeTGaz zO9tZdg(-a&&81r2y3+KFO*Rjvn3!&<;Lz?4yb)sZ4rtniS7U9%*s2#YCB{SI&@V8a zG?q`jN|DFrur@}KRUet^{;EFe#UwCU2@^e@BuPRTvF_UKuL@Y$7V({seYhAP}Bt+RR#fML(H`&Yih#>5eu~P(GGTR`Zs7L*@i13 zZQgTWO_+%p%rNJeNHazE*d6=S`h5_NGTD5&+&1_Hp45~d@_8qpR(`$@v+-g4{t2c% zCVg`+*Dbfn2x_e-FdJ_TFH<`<8hI$6pXH^R#mVVuAixKsG5&w@iEHQo|My@B7$$|; zE&E{p_IzJ{V}5J?xU+xlS2x@a1&*y5uAM#p5x@AzSLEs6m*&^xFNuF&oL`my!SDax z@4e`v7d-Fz7hLeLhh1>NfBDU){>Cq@e`x$uKk122IQOhme)&YSg7JyTsb0UoYe#kG}nh$2y-I|~9XZeAAfBw$=ZTSuPas0E&e~!IAe{=ruH|Klv?wj&A zAe_4KIenozHep!BLz9-*($;G?=@DHB*obB7T{my@V_OpKbx6Xg& zGp7IVr~hA1d&-l4{k*MPvvbZl^NcO0KX$I!{Fu{D-E{IvkKXvGU)gXgw63y^cXX>8 z;rNb@f$@*tuZ<0-tXFJrRz7z>{&%m9O*^p(9;F>KYDuwJ(YIDt9~>qw?nEQKIHv&@QckX%whC8 zFS#EfX47-V&P8vxSA_!p&g0}P)KXWnFm5iMSlpT8qJVwP4%ePkUchArh>@=shs5>C zdTl*|7gI?QTIenn1pNjEG-?8cbxw+Ik;k5Ga;vW(Z2& z+?2fvk*)A62;dvvgVkyye(A9vE?PhGnHa8g+6?$qKOFnOm z_S-P&V*E-W&WoIKYpgu}{lwChI$1-C)J*T=8aUV;!fJ*Jx4qk)e+v!-Upk^^o;*b? zGZFFqKkGhY2%9r}k!{hn~R3JV> zYC?9zVbaHCx1*7bdeJ7p4A_*p>lQEpvtI8#hxQq{lhr(Sjq(VMy}7h#((|i_ z`3G?Y9iFAZe}^#XhzRcuWnnwkqV(0~h?2ykq6J>~T5?k+ANNusR!PLKW;%s?7OLL6 z4;>j@F|O1zKAVJrRnmLG%If2IDyr{_F@d;rRhRo4q^oACI=xfx{e{hH$_Rl_>=l?{ZmN zlQ0(9k&(U^fsDmTFPU;XjNYFe1eGNZ09Kq2y#w6Os#{UXdav-SOSc9DArajCYupJ7 z>!=d~{p+cHj=AIW8HfZ_(}>b)A2;}Y<&o^?0~ufkNuZ$qz%BGs6hRBoUXLs`c=2eF zCWrk%32YUr7cc2@*zwBrPB?a~65+ac8l47m)6G#)wE(iH{vmMkX2w6=xR(@`)&Wub zVz@0d7w^!~&bNR%Z%?&vmj}JgYNWTz(c8Y`+xSmPJVJGcKf8u7|Bc0qp3=Xb)vVXv z2(0k3^v|FS_V;twihq4CeAuRWaVl?EE%NhmCjzIJvf06sHr|mS##52VbMO-Ib|%Oz6??5l=$Uz;Dvv*Z?aNcn2U#OR{u9 zf^LUPmm|?E2Nkm%JFGSRR_uy8R;GWW%;95X`0>YkTl#N|NBEnKM@ZHiwf!4Cre9VsYyQJBbr0ZDBD92H>ww}FwuvV85->%>r zug;!FwBt#2xgFDKb}oC;utPbHxp@qRdiG+jY<}wMngo>9!{fR5G;Lw{k_o_npPdQ# zvnTC1aB*sBbXhUCwm$xfvy%#2q{mD$v=53jIvi;!MChk6x6Pv{Wd{osxV37m*h>67w{g%OM^c6&(fG_w2O1ZsYf1iY2ITl#Owdl&oKn^A*+^Yabj`gJ-)^$=(Y zF`|&mqHj=$h?jfDMdaXB%a>r}fI}>lps~z&d(2HS4nYKGx)uA&&Y&+nsHkfZA*)EY zH8`e<73%rybA}uWdJF?zZq)%3*O;=Wf7aRzp%E><-%9IaT7$x+AK4ZFAgiZ1sXTqc zC^=vJnBJQSl$I$v0--KCcmn`AUZo6x;8Ufkup=Ton+zYU!ul9xNNZp$SVUS>aSiy4 ztfV(_ZIM{b0=5*>BIM#q`W=MH!jw(P+c#ZbHt)Z_j3xFAu5G`Jk-U}z?&x?an@akl z-TX`*wm|%`R!fEx%gcyjRe#s0O79J$@uqbhafqZ6EP^+Y(5+Kj4vb!k{GFhAB+Nlm zq;d-?vRJK=i|5`2>|e(uM)t14V{Z4A^3vDSzt-CzM>F8doo=0jOQ6E!+N5ae6hu}v zH8qw5jf=3GLE<5s7l}N9=N&fmWzmKM33@^ZBlMglo05MA;A>oUdLFR3xHcnTZ0VB% zbm3ulwg9L8YxCakF1<5Cve)FjoqD0|L2}WT&v#0QI*{|3dU0VW(opn?;uOR5Qgc^0 z)_A379r;d!Dgtm;N0eZFcI)-hvy+EX&@wNCIq)mS9M~d%SoEy>1oz;%@JKSAtX#8X zf{UxS*uZE4BS^%mvs}GDIp|8#5sXVL!mC}!SLg7HG)recnkXs;)-x-EZ=WxVJ=*wh z9r8l52`1f`5tKgY@dfJMjO^IV9QYblFxQxeLV!hYAs=|1&2ixFnqi?u#&>!u0?!Bz zuvw4Y(p=+XYl;?3_pV|RyfFic(nOf|{+>*g`jiJOOS`*Qo|+9{RMl-$2-Y{}@phrU z-={VL`AcTJ7>t66UJ;Qno91Vqmsn~dI9HB<77OO=LA$oq+i6u7Z&$Quo}tMr@4(;n zgn`&(WzvMJkIu%VL8d#fi&3h(D)>a{;%uRC7MzEV;B}GtX2jfNSx|+CB3>5Xl0AUK zBv1KpN51bsTuCVHx|i`K;z|k{-Rb>T+A70%PWbwB>TCcEj@t~gI3>YDa|j`xxqwI- zsNIRGLXpo7X<$i)ROc;dKsnN;fGO%RPoNmz{ZRV#p}hCZrAWt1pb|TC7#E*momL-@ zlt?xTU%3A=bkI<_*Upt!->7KOCTI)d3qadmNTjmuxH6x5o6mdKj+QI*z5ir)!bCuY zAp)C{6NM=k=pVEdy5WdDCY=(ENsh?q}@F0*o6lp)Lj4kr+BS z8Bh&WfsO-Rpq$VOl2dFJqZl$qkJt(|z_7)t7_;pl47d*lu=hEFtKdUIgei|#X$`gz zZuDsf^tlGx4$OocRuni|kO0!cOOzyWl7sc-4MLJ&fyb-$iR~qzkB7~t2jJyoJ>z!{ z8KkA^Bb^{c6h${+LX6}rzX~`V$q@!ErMJ>fLCSm40&}1tT!Tq@*?`H+aO$bER}(wx zfrZ)&}xdrJROR|eML}OhUOyhK}_s; z27gjH8w81~H%`r+b#X-Cf}X?VJK9tSUaFuO*rfMELWHALWLicZZw7-c%zeN!r4zUJ z$3iqb$f?;LNx<}p*SJQdBKg0*!d(xx3&=wCSd?=~V?m@c&eF8{qe2QZANc-J?7ZyT z8Vk*<@*W_zbw%cUQ`1q+Gj%qHr{I}s4|N7j0t7}CX_7w)GXph(^RH>(`~b{?ECr8wWygi(!=8`IAs#;K7?R-9Ajr7Ed!wT<%j`X zn;1YmCI^5;b8JqmXL6G!aSVA}6UeqiSrOW^h%ot;_D^0%J{sgfRh$f2a~Ke(T_Y)e z=)Ou~xIFQ6GLMTA$TGs8M?mAqt3X5usAr9$+KNjqZ$B;X%UoWVpZ8o_%uXClMgN(> z1r8n0AgAgwg)`v-*T{=RpD!dFD{4ofw|=Y8}>-OlFHjY%w_ zTY>h?*Iy0~%X*iCH>5_9G3I7P*DI82TjDCweiUbSW(#s6gSX=>v!GSl|v6 zeY4lHl0;zVC!p9^g~yAfoiC&ZXf&Y1bgvGXvzWToyIPc{P)hG5!A}l_l#&FZRD2gQ z86nm7EmJt7f7#;HnsHXd>BOJ|w2rHVvSAvz9-~{87y+%N#bXU(tTOy8$TTL`!Es+y zpPY)UeYPApoq)EZFy%pOCHnV{4XXp=!fQcl$kQE(h|JQO>$RhllVKPTgzhnzO(&M5%$N4`~( z7#+`-59W$&7etJFrK&cWXt6oV zFx+Q9=KAh|r$ijCfjE%|(Faw$Na}y%IHp2cTs7@CL`##^9a<2XGmMMXr^rSTRDmni z-n{qQK`)gWLX4Xw_(75E8eU_A62XsH4SHh%FA&6B4)C9x5Z?`JVgOt!^Z&=8ogC3*;%sY5PK;&~Nbe6o;zJ=W`=M%zIKs>mY7r{j(tmQ|-W#w1Wa8jR+KfExRDWyX zTr~T0_%oi0s&$*YzPhA-KoMX8v`;S7>!6kDtC*WmM^XI^@MURAuHD^Ui}u}1E@G$7 z{G|kS&a3P*0UL48MGk{41YzG1jG5vOc8lHSotus zjwtIrlQBf_@+sn{^smV_%by7M%$ZHPIz@zf&s<|un zT#L4}hcfPM({K9m!rSl#1S6IVawd4d+4;P<$>j2uwF4kCJ6$qpOb^;RRYYL@9no-&1ESP!LOPdv#y>``O+kGS_mF{PHwP=K z_<3y1X(e;gVZ>Jd-D*cp!HI6B^7gpa(H*(_SF+(l063Q#)4(5+gfy-r=5vSnaeQrG z)(UC`S}ek}5);IF}*t zLMiAA2nt_kzP#{9U*?6jd8X0{SseMheHQv&(y-TO;`?}WH>AV%TlNHS5!)MQD#9qu zGEGoSd3rs~3)`x@!+GcyYs}*mVylCO4+CI;2msNE5Mz9R+yDnpqC)CBp zV&;+{ma<{{Ku`(GY6V4>h!bi7bUzM|cg$chsIx((EIdRv;rmFqFia!Y2Mavh4!Te9 z@N>VPRMI8{(2kX{K+W`$4UQE?R>_O{J69fRX_ z#6dztp1n!w1wKXtpPeKdRVq(ysEcqmor(qm$H_+tT4%&o5ow^~MMr&u3U%5@ab1#! zw2AZ>^)S@rgH_r+1d^CQU%7D<#1aMxG!1`^Ok}@RlwU5Wux79h5+Fet#ta!yBq zVm?Ph+#auyW5Wiwb4Fvue6dnfz;)i&MnD91uHX6XmZKOQ8@*U2<~}`IpCq;lnGbjx z_@Y{pLn{S!afVBV6yrYxDPSG+w^yx4>WLMW^Cs6n<>c5AnR4WKjCYp<7lnf4mG%y<7J)>n0tON&L_A|E2E3priD~diwn^+F;I*PN?(g>Nx z&uV~i=~zlw6N-9^HQWF!AP0(Apj(S4aAfy!>-}4{e|UU7z5kK{=^D~5S{z!;H)7w? zEJ8Deylp>81!NCAW%Xcvy=-)CuvWgv_4QAS+)sRxv61Ik6FGL@NFNJBIv9~D6x^aP zz*<|gX0Wv$HHcf9jZxaju1Nfl>^ZeMHP$|@mCkAwWSN8#U|z#=ex$Z4O8;`#7&27Xg|wEeD3 z14qn*Z0SyJLApJB@e%`C{c2%Fv=BHPB556-z}EoWU)9b9JP9R z6ov==`jaQ?UsNp^sT7m-Di!vnT{PB6v{kn^;97o4 zy^`fqk@eUh3f}7)iw`C-85{}?+cUrPja=NjRHN~5*Bu6!ifh<8ZLoqS#R9a`v4I*F zUsg1QyNJR{Bvc9>?>@t`y?F3+p4Fx&=5*eiIvRGAd3Qiigbo))(sX4PO%W9ual7*X zb~G}PFar8f?2&r>Mb3aqw;$0iD~dDJ7wMM5|M_ z$~=;14B1k>W6WM^xG4#z*u$AGj5KTr?4yvYUG*E`w2VHeIFi7z`kllc}QRg@8n%!{6->gRgnZ5t`;XJ z$n~xFcYy+tfsDg~o&cj!xqI>jr~Ez0V7wl?C7lS~2!`fip4srw9XR@Cw;k}zH~c-j zQ~^^u_f)t5r765vQ86{SsEN6$ZMX9$!{-!GiTGv8dNngYoBIl;s zJT(;za6&W9TffQsQ3@ykouZ-B@}=0TOmye@8U`)Jx`i<4v|8ZEJ5GZPlkg{l=rn9Mw2{tgO)ev$ zT#ZPm`!z>_P;RwQXZyq`6awoo7V7iL8no;Oi#Hs6A}uO9M196KhnV+)fESVf1A&5XL!HqZYYV z_DLcX*`x705P(d4ez$3sf~;-a-pQ%)HC6=Q%Y7vkL+O~W_ouJD$HDlIFE@Mr`HR<| z-#dHpz4YcNP4&LA_dM>g2Y#?t!+V(W1j2=Je_5|3llA_1B#VaQ!sgy{k0@XCL6VpS zd+04pPA4JmUM!ci(I#e$c4q>9?>R9llKy?1#BVsLL@&DxTpX{DOIm86p^_(j(`X{L zxJqp$hGI@T(i-8mH&6Se|L0$Wm2#5*maLL?{mlupmW)~`Js{q;z~uQhxs8LbSPT$o zQZW^D${wHt^%VPD<|cN{P3|VCoC^NRn-}6Y-Roff9u`6Lt6qB=Q-;H*px3pvF)&VK z^oY$ewvwh^NF3MO#dJq3$SS8JjFp-53nKJ*fcSR4tS7#$N@e)CdqIRrd=52}qoV^< zGQU!ZA6x~y-5^NI3uF5Wa8DT(?oDVjDkPYIB!HvH+!d0J_y%{jwKU|_1HxZViTtK_&Z3yjKiu+4HE@;=MA?zPcO)_QOI)@%Ym z8vc-iFJKy=H95xPuQpPTCMl=y=a4*Mz6>s|pkGC@j| zhB~Uqi}4Xk0NGG9(-w&8^HXY^Y&V1X>#2Y%0Bu~XqBTPT7io&fc(9`gY*$|(=|xZx z@kMw;CMimY7D!`ZTA|HUUqnE~>EVE*`b(I^`b2Z*;@3MCzIrqCDjDAiIYD#6mWnA+(NQIJOWZ#nq(N(N zEi^|WhOT)MJaZ|!AlL7TDt-GXpLALXG4|uN$Q6oBa_~L6Hwg$B>P?EKTy|#RCzKqtcDH!eJS$ z%v4twLk_sQk9-NA&yz>N4rZP=j7m(M=oqEaWMPcy^%>=(9JPl2RQ?qb`0Yf9<3_~$2tb7xg#*@ zNQhHYhk-Kx;WEA~I|l~=@1yzQxv?1wl7-0|VYMirjr~mKvdTMAl-pY+eqQ`@r0-7( zJG$MM;>3$~+^N=fz%U;B*yk0MU&6}3AP&vcMaP4{(wdociJ3{DL;**D|J+jIDa1VQ zpBoOD$S*@tfcH4`PKTZB%vMQZ-T0XELSJ5`ufIX<=3)6^m>r*OVav29np>a|J)H4R zR(+W&C?tX1Uvj`x7ebZd9ApYo3!ssgiRpFfkUan!lxig*yn<2RT%JRj|6=oKfw5~{ zcc#frY6GzL!DJAJ)t6b)*e1eqS!F(q9~{^Tsz1C^Gw7y(s_fd=>nJ2kf-bUh)PnI4 z7d$4fA+mSW!fh|H1^vkaUF09c7;hmSlqB;+sK}ffCQVr4Wge7@3VhMweWgc(Mb9`@ zF!lKQA(sUddT4bwAXb7ul$uJOeZ$T9k{(LfnJ3lsUi>yxhV_~+;)3MMF_9ei!A{C3HyQ}@Wll%XV-C^yB7}$U)HUcGfiIS7& zWiu>-oOAcnmS9+!?0>Q&6z!x^YZ?(G&wx}=2f|I`kowQCFvmbXiQ%{2e?7v8s6i=% zZAaAE?0_5y!d5vE%S;EMa(NCZ0|;LhqZLK5o}%Z`BP&zS2Y~KDT|c}1z0|eRL;fvx zP8!R>uRP0F7#l@uDR$A@GLjvt2JxDU84w23<^DaYJdc`4#iNzqM~A{(y(tQ2EP+Oy zEeYsH9G@`l&WFY}W#^|}nN)uPqk9if!syPSrZnE)!B`2YJu(Qez!$dL2f!Tq!9*v=)O=->z3MB5 zXfLQI9!<9zi=bub52D#fg)Dr9!j zS4O>0wmO6TwnN}?z~MNg=*=KtlB;>0!nNy%_ch9+OxP=oT&(4|!I-33%=cbpn*H$2sDl zVNcvk#+IO%TomThf&$hB%ERB29LOq;072}sCW>wlbzfQC5Mcm-i=2n`wepPUElfJf zN#BA}qHKxBMRr%O`YP^I-VIhwZdbod9DLL!y_)BEGpTOq4AF1ccig|Z7%o$$#wg!s zMTccJRB%6Lcr%~zdU`RDbiDSn`Y!4oF7sAf5?h{UxLM)Poiofo**-Av&zO{y27ZAO zyxjT9)0uUTI2v>>%MmQBz`{JyzT0_*r-|wW${u3Fn#@K2SqTfb^q-klv|UULXJYFp zMB-Zv#va4S;tQvJ!K-VJn?tOzCM)S0Nv!zjFz|z)MNc5}-Dk&rc3nI>trUK^8$S|< zJ0<^+ID#LZX{|q-6GJ}2Z!{zkJn845q#lXBP|^26VSoe3j( zXYglwd-Ho7W&=G4!}Zxc*MoiAsBtH(B{ksGZT{p-2a)-22br^xknnhdYxPY!IlkOi z4%RNbm!SHJ&>2SXq#AK+nd#uLxZ9rMl$>#*Z}C_E1qf4txU5mCtg1a4OLBO8CN`Vv z(F`=36wNeA9}{@8Df<$G3;bJZEXGqyjYTPBG%9cqWSoyoy%|*A00E{>;0~n_zU2Zx zGk_1&_a&b}jJV}{clkgpV!DZUr=GhM>n*?BBY^VeCFB9~r`iA3@#f|@2*}(`?3L=< zfjnq!ijGx1{GlVDjfS^mn!`RUGi@*LQ`f;!Nr}Y7Fy_}~lruM8lh~~SEVy!Pm=xX9 z%C_h*FFF)(tAr5w_GU8T+svr5K06(0zbQLi)}-Ly!ryK>c&EKxjBa;SD^6!8xDrig z#;|t)Y$*3v_Yz6K0kwZH%Q_cy1%FAI!Rh-tdH+VD|Hq#P8y??}cjWk8Pl}ulBWD{3 zhL7)m3OIVaj~Pa5Zu&IKW@t?>#CN1Vp4X2Nm3OYGmuB{7C zJzr*1x|`Qd9!|IWq)#38;6gQrzd8AFMql^#M|j;D2>DgRwG2rmd#$7DFi3{|q}9l8 z?i-fJ`*|-5v88`L-C@CPrxfVa>IJHfZf%+|aSr$p*9^OM2;HBC6O{GRf9ZfCi;#G8 z*(~2i_9BM=$q?(bihj7l_^8K;TkGG#HS?a($De_9;>5Vzu1L;YT$R9)I*N(f8$_nJ z(M7{h=$*WjmY~T)QIJj}$|Nq(U>3Sc+6)9J7I4y%I^g6p;$gYX!IB_cH#ucTNRN_6 zAv;t?$ZjM{;PB{FGYL^axoe1i7M3)Ro$YJf5?1|P@bTaoy^{_n7WMO)>bo*)`ag6Y z>F)Qu3)GHy*ZQ5CnPYHn#)d=33gGg@zn4^z(WJNx|_)h3i3{=N|a2ZT2Y*HYq)R8Gf= z2CG2>l{3Pho0(>l8**oYl;a;MQ!70(jL7g5qlt$hC8wRIUV^YjtoY zEFm8YnZ@$)7jIAX1Mw%g_@GafVOqoNr5bJINhZo)YVq3 z6w)V-C5D(&j^5>Is&lQF2!`+%w+vkjok^1qa=NFlTZE&X|Ip5tp==ROkB~Y_B76W% zTo*9qMfuea`<^K16Eujz76-jN%RGN>Q$4>q8Gx)p7f*e}u%oxuf;4As70H^zRrD74 z%EN$JjH#Z>h(JcRT{0~$;nhI(gBAdrf^Cqyx}t9Cvh7dq8K<=7)fY@{v3$B7B44Jl2P3G)1*l z7`!EQ#^6m)>TgKjs;BlJ+S||$A4YH(w~$pa%ue%?EEg0NmnNe%bU-&eLCxm^CpdvvL>FuL z*!vkP)PbImFqScU8d!t{V9GVZtnHr}x_5QQSnmuvJ6z3z=`RdnVF2j3Vn>*pyobF; zFGggrQBMZ4!C2)TO_Y$48%JiTh?f8o{0#ghYYf0-EP=aO*Ep{?ep_2$XD_K~ui_n`F9~&u+?kWq&*FD!=z!tfdIp!`O3 zB>bNun)XUGDZ?u{!;A`Ozz^ez>f?am*hc7dI8+^FP4)9++wD(1mv(k*@ZU!SdMr~t)R?%kI& z26~6lrvVfz9-8Yb6+^*4#xvPcFzB}goH0_8pDqCh3UL5ew*iE~+7^2_=5)4r@*v2# zs@^yTy|Zg`9USR_EM>pJ!^DhyTTpFX5@%w@+Yk>*niq(BY|8cGI2eY=I3XuoWhtD`(ScV40)w0)wP30 zH~!5Ys&$AT5SeCBQ!qNu*Hw7fPd-4z%p9scMq)n)?jt(6UV;?~F*)_xh)7re&F^j_0 z)tY;wmI7{^UX01I=;MNDWMbGf5*n=@Di{W(3mYo#BCBfoRIE|ZcbK(&Ba`JYE~TW; zV?|ks;8sx`PgMZD3;dwaTyUK5@c!7Rl_hX8KB<(90~owy$i%U6_#)E!k6{LF=}H?8 z)&t{N!g{z^2Tz16!~mJ_RT=zHOCgd`Q*WkkU8=~RA7Q@mqcfu z$fIo5h&ENnWR(+o6L3?^hYg1=uFwc!nrb-z?0mQo){zX?^pS^=VscP#ta&?rzM)+Af5~zk`S|}yHot+)BVnAdSnWe{eXyqN;4ng09|Cg zxg%<-hY{S6tUydfcx3`Ibw+*r)K+UU-KM2qxS2W{^3*DkvN@G@1XRgZd6Y1Qn+i~J zGS!`8+Lk`R5CW-=Q5M(#8?I1Qs zuxK)s{sf}&3)daw7Y7!&A%0CYiASo#fWT2&Tl;u+ma-~cgHc|@H-;qY;*&WP`NIfA zz>`=e$_V{%sY39JUeW|7pu-97-(oI81JkFa_`;&I_+xz~bj~gP)121raCcQf{uoa~ zuM7fzkPpr<^M&S61WOMkhVmc7)j)vYXi=){%&=h+cEAiX)RBkikN_OZdJpxbevoz9 z97jc)JY%g{zPk#wEaZdZ&~VMm;Cr#Jzm{LQ+A6S#7j`}PE7YaxlzhDR3rU?4)(c8D zeDlWc7%kIA%VoBLc~}^1>0ma8zV*I?ef-IZz8Kop zXD435^3`$@b#QKx##t=skpnFq0tgUvWK)wc9c-G-5&Gn~8uk6suTm4DOoKs?2t}4# zvUjZ2_vi<$+_%_C)Bm+(PE1h>pS3v@arLprn~9KEP~=~mpccWXwhZt>fm8aWz$sdN zs=iBdr+JNdRt>U^%MaYiHA&f0gxiD8fji*(fCHdf$RJ7MgT+vgz4~!fns9+J8+gQ6 zO!!q)%h1|)pen-H;1J+A^J*D^G9?%RY&yash--bZBY&vC_xVJ2L5Y705(gG|!jxSR zatrEA7?su?3QsGIY0k36qVd=6zU9K5glI#}$}duV)aaR5pZzUg07XbPHQwe}i^95C zO~$WPXx}?m$EY3~;<(Q6Gp4=Kw9+RF-L%Zc>(z;pnov-OV{7C4Rgbs@tlASm$0%5H= z<~)69wbSF5c2C+z+x;)K97jey8zXZJutN+KZ8#!4H=E2yf;CUfxO|UbiHn9jDPsw1 z)PFE_Vv`{pBZ_nfyvaUX^eV7gv5{C+cfXQNrD}vy$ zeC+vYc}mvsCd!~2`T+?{{e*lUhoPQ>0SkX;2b_(Uj%V`BKGWsmO)^=K&qo?8$I~^? z1iz#!`AGfBVU6}Y#^qrLR#$ne@S~6Xv@y3KzBX%>+H2p)vBtKq>`#6YcC3NLRTdbLMTZZYt_x0 zAV~AD8Lb16uk@>i*#T6h~!EU6wIN1$bV1GZl8!ACf6N^8GwIu3e#}M+Y&rmRDMK=0z%}e z;Rd4C%K@d#mDR&OA;o(}WX}EC!a}Y9w>WS73sy;&*KUipS}@ za*tFg=IA}@8Q;>BZ20eA;YYS?^BZeaF&|h|v_sK>(sXY*Y4E;-_Y1PGr{yPYp&A(`3y6@c(!(RdxXd9H2nasoPfB4aj`?4}8_m}js~0i>)Sy_xF)|wM_KAo~ zphp)x8<{I5pawRkMKX2}??Ti+6NfNYSZp+#g*z z9M}-c0X-ulyw2d!pOObl2$c5$7dwjyf-p*_&z`tN9{os@#z<<_iW86HRW{?x@pAP! z4xm#V-I<~XP|CVDL!iBg{M9q3nmeuFPwfJ91>dtfpUW!L?2xM^JF$R!HHfuq*ro6N zEl4RSF4ah)w#bv1Md$?xYq58x2~~F_`;k1{`4C{7VU(NRXI*+aIg@wA^{Rgu|FV=I z8>sqhiOH;@`D{tUJV^a2ydzkYg%Q98TjZPdpOPIIqS5h%-evq#bcxiC&I~V3BRAiv5CDL0Avr4enQZKMPDQd7+JW)i}0n;rEJllKEqVu zL68y}2Ut)Cm!&6K?f&%5Y6#9*Cb3L<;Tk}!KC#IwHYGpq0YWqi*^>VF0>-rsRZ?61 zUi!jb5_oa85CF=$U;S*}{orR|@&nxueS~){Hp9j(;`a=DtL|CMVU#3QbHNtJknBwa zQ@Pd8X<66qDC^|yXjYT~T>>QC;RRq3u;_I-i&L<#);EGDJsUp)kv2i-hv zJEeI=4fGbo6J!F_(5mk3Aj}W|xu8F_&$rnw&``_;>3v$#WA_s{58QqNbHMsc=*v%p zMU-fX8w*;BV|d5fBClZdAOF7$HhFY0KsrmjVY>&nZ^zubwqx!I+Ld)L%K(*!a5Khl z(>S?qj_SN7+Ax&SWB5s;@v$gQQ(+LX5Vr%rTAg53qnLrGDJKczgo($p^;N za(IAC_6a-;H38u3nmUf6pdc5M3i@rq%aQDdSyzQd;5|UrCO6|?VC*GHUnHXw1V&*e zr4+atu!tPhF~C9&p$*KpAi_Y(=kY93hYB(;z}P!TABowZ*jUJ*NRMI@yLrFHH5LGK z!K36z(nrcWQ^5_s7f;Ri^99HaNX!boSpPjp>=#4!YqDROb7ar6#f&Lm`Noy`wOk^< z(l3yP;T$1|R3`t9Qh@CLi#3~cgS}z?6kLMBcQ8Jcb(0gf-m*~<>#x6(>{i;1`K`|3 zRp+O0O2@$Ar7aV&BjCnvIbnp|lfcKZ6uX*Asl#m}<$Pe?Ff0OaVMnJ#)Mn)jhrTcx zZ+E|m=dTRmOf9yGQyMnHa2a8R)$UUY^M*ZbPTk-Q_KqCli|q;kA<(xmUv<%jQM7E_ z5kTL1bEG#<89qnm2wTy{lWp1UCyUZv$QO=qb~oE(o6=b)x(i>`P1$P6CQmv@84RVn zMR)UaoPM6}H@jUAxcK-teNS`o>)IzDe5K1=vS^v^ ztT%KB+*h58e>5@;Jd)Ew`Z2t*i;frDete`{{)*`H)pGl>2^FySez6zp)hRySk4H^@&hV!(!`A6dH*MkiwQJ#&@MH(s9oR zXgS*30v{k|Ynl$h!Pr5MY&&tQdnggYydEc5u~ehH;L#I}$tJXtJ>Icn7Bz+Di2Rd=741&NtmS?8j*NBFl48#tbsgJ@` zWCU_lx><}j627T|;|G@LxyWO))^BT{+^k++?0*r1Sv8Zc(Q&!x{@8%lJh?wti(j}$ zdNOPO=X+ibu|!X60aY`PYjP^9-5cV7ztv{_neN)5EqFubWtG(&CZv?BtTMKM9W@uH z^;B!NFb`%Q1Z?zFSh2a+wOM}iE!n~6R5-2`%r8nBF8x)eJVf%ZhTN{va8MSRtVBlD z{e&7OC+HMsA7sCpGn`q-Jzk%d;~)QbvKY61zbZ5OK3P0|Rpy$=wy%apwta=cmw0sE zk28aA*A31A0!g#P`oer+rug4U@btDt+=!=o!eIKbE*%S|%Leol|H;?UbrBOLqn{Fr zWb{|Fv-`z^ScplojJ0C~PGoLjocF@ZTfOii_X=85tgIyA1$O&|8?uD?UvpdRWz+tD zKE@kjjr@j2Yy&6xuKPmVnuzm=Q8rU6kAkayeA;m_ige81Ss{8IViK|t6?;19R)LH~ zlS^2um*lN^6wwb9;L>$hY}yA>Qx-{f9EVbOGpc;<-wcBi-uUL#vqF>Q-89Yy9CZ(w zCWEbG^Dy#_T(h&l*eOePif@Ip9pS`x!wnMElZcOV@pysg%@$$u$Y4Y84PFfyQY>be zm1U6+p*-Aa4zw!J@kq=SgGnWuTh#cGn<#vl8F?5i%Jwkb2HY2uMJ#Y@fQxixv!JaF zQ)|#~fcmfF0YDAo0c>&<1a;V#Hg9B!L7nJ+<@<(`BBmG61^X#li}Q0cS8({t_&{QR z7auDeG;<{540qrA+*?cEb8CLqeK@vEJ`{#(_on?A9;{#_Xo*{-AruunoNV+N2K-MT zspDjplsjPnXuX}XjNN4o|86|spSBo6OPVuBXGq$JOb_qPz3d=vk{& zu=1(vpG`~~q08mfQT`6$N0vGYYqE z#RtmJB$(R-Lr>?$Fw#i3dVewBjT+$eE2x;%0&91XffD@^x54@5J2(>^$0Y5DUA1Uuqk7ACbOu?9{dBh zZPbgUSYOa?TO6h8M%)5oa$ddDsZo7(!b1Tu~#ozuMoN8&|k)wtlF7B%fa>{GNvtFz^peI zxxP#+c?9GA)C*QeTFJd0$4)xQ&DZ=(gxv^X$C0Fv55M~+-?l%te(E*odLQF=A6pm$ zHu5dp)ar`=o&1(tS4n#0wA5{V^ZlaNoO+>4y$I*qm4y`C(!hQ6%h(?{9v7Vy#XQ27 z;|gUTJa7l4u*ekIHsjx6#!^T9p?rP`$xeJ+3YczN01HT%(U71uNYJ{px#TSh_e+{f z-)dWzT%Gm*dnqI0*S9&mei0iyu$rCl@YwPE^86B?rKS6-_LAn3?~tAH<`NydeB0qM z`gURO#Bnz9OeDP!(cbY~9Jz`-L&25l7RX$VFAx6R%!&UA# zkrM;tA!CQeDf9ZbjrJ3Sh!fuXz)ytTJm>v9$uaLL<5RZj7T5^`6u}I}9ccud+~Ftr z#Mwu|49%0ma4{ zN5lG;0uUGBbpD#-XYTsvl$7QfkE(XReOLHAN7Lmz$<^Yh>R97D%}v~mLs@kpuc)M% z>oS`ObB(pY$ltGyGJ+?w?Rq4LYVvw`E!K1@7=tCCFSV4bE#Tdb@#o!toG9| z(BZG6=Fg$}Rlv5Y&hZC!0}s2ix*z`mu?b4CTK;{WmrnK#CXA=qzMn8%anf~O8~9zp zc3$_MQ=w<;cIa0`;}8*#Xzo6(2hnVFzrX`Vf+&(-&4<2u+nk>i<1-)E2M4*XKZGgZ zp_eL$y!+e_p-UMKuTyvd=v=~B(*#=pyRbSq0UijXeb$*X?=P!gMsl6ZZx=dscZ4E~ zlE*w7E>Q>z^Q-GsDGLe_;)Xa@@no^Q{(RnMU@w*vvrfmpn=tkgk%0!pfB^Yk&0L}0 zTmMf0hl2}4^^e8M%HuqyJds+6P|(i+LLsn=U)U2tlT}v)o*6g7J|b1*-4mL89@E2) zje;&2l4vPbz9W()s&8kvSDIaZC@YQyo=p!u+6lLrxhq8exn|5vrs}@%+dFc%Ar^fj z<|@tc!P=rOjkMVm)zh~(pBj%=zq+z`B{j@9m!}tRR3ikX>9& z7FRx|2cbJ<1wTxiFp2Fi7V*fP+-RA~A)vcB4-Gu@??aO`jcr?fr{#JW4@dYAEciFN zG4i6fJbdSY3O?v3mRhVazFr%tLTG!(8v|E(PhTQP7{<>1!8*_Hv2h~VM+r-`X4wy$T!ATP)r!f z2Mi8|DBbEePc?)YEoH-F7S<4k3uAmmcL#x8JhL>;7(BP=YHh>T_H(uwG{X_u3}qE+ zX1veHD9XqrW4vn{9cr>4^Mp$0JgwlkXF#RyydP#fEv5hp!$s$St0=JyL& zO_+42DgP*VcVUkqeVH0l9xT9~z$=|*=`m8v9Uh$+O5r1FCokdcL%b6K=RCnIL=rOr z@{DjX_d)ksCk_Ud6URKV6GnDW$<9~e=zm{!_kJm_-w#;&w%xJ(56><24#0UR9ImYX zug*vb%PeQt)bASFkdURkJB%r=_zj8!R{$p{1*q14G)SglZb-s+eGDEEs&tb`s6HO) zo_tUEfR6$nlAR&}-smMm=87&;uInyZpo)3lEMt@GV~8dtzik>ytRH?9o_2gzERK&o z!b$3A)JA!Aa48K?;Z#$CGgPNQ{G+)lhb_B|kGj9fq$+TBDX_ehJ_wHbZB(dJ{)b1Y z1j2wbenvY%H0i@Fn(VuyST`4q#tj4d3tmztrjn?PM?1#2-$Rdl6A_|UBL(32cjbxW z@574HW`rQR`*Mm1md7x=_@@~GR(!b#EhuNk!aH|-D-P;}OlDwHP|;@iX5cQsZFq@%qgXUd#F2hrz?15KOQ3hlw=?4w=2j>y zQUKDH?UagRCzx8(?HDN!ww3G+2H*@L||in8iE)Gv@4(m%u^v=WvL z!IEG!#L)4F$4k;vE9Q=dP8n4wsqUF!s!WJ5@{@WSl4lTg&{|%-ICQHOP_dDuA&12% zQN0BK6g#mZtF4p^J{q?C>DP$aMgde52d~6%;tFW`IjgESjf>TjLs4>!2pe!>$%-FP zi29bCoJ&yUO!k}u56<_2tG&%WmcQr|V1S~HOLCV-ZdZ*Gyt!Th&S3m)#np!+*)%0E zRmVB)>F#FjuDQuC1oJSv_PSu|(X!f{L)u*9T(B;mkGUmT9LaFXx|LMo1dMIWAq4A0 zm=#!cYjay7nVFYPa_z_=O!lD@uRQ(-hrG1BgIAl?sSZGv&aH2UV;^SbEY$v&b$MQ6n<1*n?u8nos6uAJ3@7uogQH z331c^E*BF)&Dk$*hT%+4nq7ok8R=_3u^eeOXFm$VNu!K)7g8)X+M)4t0A#2<0a+>$ zE_iGYk+mY&0cOoZrz~8=aE((%Jd#Hl0bf&8l9*lEIeB`kU;Cs%7jhdmIcZ(|5tXbY z5!QysfNZVVd-`~O=W`*5Z+an_Ix(kM`=rD}tSC)lnQC+-TgU!ai4fSx+I@zv_P1xf zra7zG_ZpJR&U)d2yfeQG^BN_@GInB@})at38knj{FaCj*W2pi4{>6df4~@Wrj^H7u-0lWGd4 z5OY=-j3{6V2M(*BFY~c>;$=(Md(D8QToObaUO#N_iO>z4T)){9gD6I^Daiv{yc5IF zhaqi|7YEvaV5FDMMAm)n9_2ViUa6m2B63-f;jQ^Zx&S3Q$`h1>`$fau;ml1`HFxQ( zDU9suv27V@eE05{t>Hng8hZP7d<@7`s>)ls}H;C|^L( zI$QSAVxyaDG6b~+(N94+wUa32Ip{w6nY{bqhY)Bi#-YDL)A`YiSye%gbPwK3H&N(x zK%y(k?GJObTfWe4N#)#R2u-+k1iZte4(lQMfr79*?3`X%j!wuVpiBqMW7{+IsTL6BJ)UgX^Xjjt=L+3>E~J-l4cDYi4X!u2NPa6aaZ`P@j+8g z;!gI{^dvxA2|e$d73U}PEJ9j9LIowws7SvJ3bFeSDjg<70k zxowmwW8^)znLNnu+2?XV4Q9#Q0Bqf`CTtXC&n7Zj{j=Hf=fnoBL<6hH51Dq>aKc!~ zpYDfGVNf&j$QK$)9ZS25j3<|u~cB6rBA-b8HK~m`PqE1 z1^yhyL6)jI)Lyk>817QG!nTLGzsB2u*#b%gKTh0gQrxpxJwE5zsR({^jyN4OXXzA> z%9+IM2c+mEW>F#gLlE)O?v0i}DF=+Bpa?>EpvgXN6F>O3p+I>Qq`dR1sO1I90Hkzb zaCtuW^JNAl8CcEjvYEetup>4ySCnH@7DCyV(a7(A&=Fh~^poD|e>_d&BzMYQC#cOp z-{0L1O%IqhXR{?{vpMJGh!KYApn#KDm@ct^F@r&zfRDJRE(bNjk+OY$VGXek9?3!3 zOd4AGCU6vWA#IvwKa&xU#BI|Ka0Zu9yC90@!fxe6)0o26oV!8tJ(%gpqA5^Vq{wQ{ zV%tShW$2{^?uQa9n7fJa2}Ur3K+>h5@f;&#ecF*tl6GeyTq9~#0*t0IT@-%@got&%>@X|V1c`$cx%?2 zAJ%?DwhaQhLGw4s(8J8>*C3iv^^1dKoe{+=)$*7s!XVtn0G?IY-;jPO`GJ2Ix%qd$I|wb&`-c{6zT`Y}>mr}soxYgByT8F_z+9DqNH6EG8; zVlg2B(ny>xpkikWkg0!-{&Bb3k;+44?m+$J$XvM|+p!4~imY}Z%B+beR^1hR(LL5d&-P9pl`Vc-!d#T=_Z8y3QGn6$nFNf!@b zPVN*h=V?9$^-mJ3z@GhI4T|xLl&|~R^5fBnOL7sw%%rZw$K9cr{RzgHQbrMkL~vdm z-?&O=iX*I&jmV^*k(riIz0j$IAxcRgd5y+TP!gyqV{16Cn$boKf~`qRVQ@XmJt+YS z&LqInz2OuAHAJ&Z1fVZxhF@E$9(futj%{3GCMYf-L8|l(q4nl^AtuQ@Z+~U*Wb0Nj0CP9xU_vYxH>{VYhUhrLrq__8m+O;5iH6PgG<&wPHc}@%Z(eYg7v*56 z+`25wWJ4@!#NXj8%$En0qufDjavNonenE%b`hY9_gLMYP$`SXsC}Xgs41VFX`3LmM z2H%ju#NUvW$LqWOEROHL`z|jA$0v8+#ntw1A3WaNeV2C1;9}aZj~~6esgFZ>cOGBo zB`!~WWqckuUVZ1w3WFM5rK;#n^Kx_Dj)!$AV3%tgro<) zXG(U>@Sl}+yEh(c_kAZf2%3FY(p)6LG6u=Y&6PfLWcEcpwa&MF?5 z8B}pxZ2o~DqR&V>VurxJ&y+*IyU>7JOc+L9gUwV+q%coRLqtN|`H_W$!WgO0eP(%L zVTJlq@Zc?9nKug62!XX;MF{K=S58v}#3*96=ueUe>n}xWNWfr>wXV>$js%ng<`X^@Ni*bXSr$tW1o>;4KTIf@X~xu zd*B}wSL}jE>}bL5ZYbpzTPE6RQqCWJ%-9)d5}g>+L|AswArS!=SqQxx&dw&Qnmn

h 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∓1U^i@FH*W~R#|M4Yo*4vdH|cju2;i~D@N|Nc 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

GKfIUF3yDtzQZ&U{L~#`;v|C&u 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

s_zaUQ+)Zz4orX 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%0A~XyW9iAfsK5y~3`uUd%h~Vas-RkTW-A}>Pb@!k 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
{yF{}7P0D4QYY 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:_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..9cb5cde --- /dev/null +++ b/windows/flutter/generated_plugin_registrant.cc @@ -0,0 +1,32 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + +#include +#include +#include +#include +#include +#include +#include + +void RegisterPlugins(flutter::PluginRegistry* registry) { + AppLinksPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("AppLinksPluginCApi")); + DesktopWebviewWindowPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("DesktopWebviewWindowPlugin")); + FileSelectorWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FileSelectorWindows")); + FlutterSecureStorageWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin")); + SharePlusWindowsPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); + UrlLauncherWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("UrlLauncherWindows")); + WindowToFrontPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("WindowToFrontPlugin")); +} 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..34b762f --- /dev/null +++ b/windows/flutter/generated_plugins.cmake @@ -0,0 +1,31 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + app_links + desktop_webview_window + file_selector_windows + flutter_secure_storage_windows + share_plus + url_launcher_windows + window_to_front +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST + jni +) + +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..98e2a3f --- /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.fluttersdk" "\0" + VALUE "FileDescription", "magic_example" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "magic_example" "\0" + VALUE "LegalCopyright", "Copyright (C) 2026 com.fluttersdk. All rights reserved." "\0" + VALUE "OriginalFilename", "magic_example.exe" "\0" + VALUE "ProductName", "magic_example" "\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..3524ff1 --- /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"magic_example", 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..3cb7146 --- /dev/null +++ b/windows/runner/utils.cpp @@ -0,0 +1,69 @@ +#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(); + } + // First, find the length of the string with a safe upper bound (CWE-126). + // UNICODE_STRING_MAX_CHARS (32767) is the maximum length of a UNICODE_STRING. + int input_length = static_cast(wcsnlen(utf16_string, UNICODE_STRING_MAX_CHARS)); + // Now use that bounded length to determine the required buffer size. + // When an explicit length is passed, WideCharToMultiByte does not include + // the null terminator in its returned size. + int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + input_length, nullptr, 0, nullptr, nullptr); + std::string utf8_string; + if (target_length == 0 || static_cast(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_ From fe7a0cb5645e2c596d76aef9449cb15b7e646b2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?An=C4=B1lcan=20=C3=87ak=C4=B1r?= Date: Fri, 26 Jun 2026 03:27:21 +0300 Subject: [PATCH 2/6] fix(preview): live interactive components, real-size screens, no overflow ComponentsPreview is now stateful so the switch, tabs, and select are live (toggle/select to see real behavior). Variant rows use Wind's `wrap` utility instead of the no-op `flex-wrap` so they reflow rather than overflow. The feature-screen scaffold drops its phone-frame MediaQuery override so screens render at their real size in the single-pane catalog. --- lib/preview/components.preview.dart | 52 ++++++++++++++---------- lib/preview/foundations.preview.dart | 6 +-- lib/preview/screen_preview_scaffold.dart | 33 +++++---------- 3 files changed, 45 insertions(+), 46 deletions(-) diff --git a/lib/preview/components.preview.dart b/lib/preview/components.preview.dart index e8c3bbb..f2fe052 100644 --- a/lib/preview/components.preview.dart +++ b/lib/preview/components.preview.dart @@ -18,17 +18,25 @@ import 'package:magic_starter/magic_starter.dart' Typography, TypographyVariant; -/// Components preview: a curated matrix of the key library components rendered -/// in their representative variants. +/// Components preview: a curated matrix of the key library components. /// -/// This is a static variant matrix (the catalog renders it in light and dark); -/// interactive components are shown in fixed display states, matching the v1 -/// "no knobs" decision. Selection callbacks are no-ops so the snapshot stays -/// stable across rebuilds. -class ComponentsPreview extends StatelessWidget { +/// Buttons/badges/inputs/cards are shown as static variant matrices; the +/// interactive controls (switch, tabs, select) are LIVE so the catalog can be +/// clicked to see real behavior. Layout rows use the `wrap` utility (Wind's +/// Wrap widget) so they reflow instead of overflowing the pane. +class ComponentsPreview extends StatefulWidget { /// Creates the components preview. const ComponentsPreview({super.key}); + @override + State createState() => _ComponentsPreviewState(); +} + +class _ComponentsPreviewState extends State { + bool _switchOn = true; + int _tabIndex = 1; + String _team = 'engineering'; + @override Widget build(BuildContext context) { return WDiv( @@ -63,7 +71,7 @@ class ComponentsPreview extends StatelessWidget { children: [ for (final ButtonSize size in ButtonSize.values) WDiv( - className: 'flex flex-row flex-wrap items-center gap-3', + className: 'wrap items-center gap-3', children: [ for (final ButtonIntent intent in ButtonIntent.values) Button( @@ -75,7 +83,7 @@ class ComponentsPreview extends StatelessWidget { ], ), WDiv( - className: 'flex flex-row flex-wrap items-center gap-3', + className: 'wrap items-center gap-3', children: [ Button( isLoading: true, @@ -96,7 +104,7 @@ class ComponentsPreview extends StatelessWidget { /// Every badge tone. Widget _buildBadges() { return WDiv( - className: 'flex flex-row flex-wrap items-center gap-3', + className: 'wrap items-center gap-3', children: [ for (final BadgeTone tone in BadgeTone.values) Badge(tone.name, tone: tone), @@ -126,25 +134,27 @@ class ComponentsPreview extends StatelessWidget { ); } - /// On and off switch states, plus disabled. + /// A live switch the user can toggle, plus a disabled one. Widget _buildSwitches() { return WDiv( - className: 'flex flex-row items-center gap-6', + className: 'wrap items-center gap-6', children: [ - Switch(value: true, onChanged: (_) {}), - Switch(value: false, onChanged: (_) {}), + Switch( + value: _switchOn, + onChanged: (bool v) => setState(() => _switchOn = v), + ), const Switch(value: true, onChanged: null, disabled: true), ], ); } - /// A single-select dropdown with a chosen value. + /// A live single-select dropdown. Widget _buildSelect() { return WDiv( className: 'max-w-xs', child: Select( - value: 'engineering', - onChange: (_) {}, + value: _team, + onChange: (String? v) => setState(() => _team = v ?? _team), options: const >[ SelectOption(value: 'engineering', label: 'Engineering'), SelectOption(value: 'design', label: 'Design'), @@ -154,12 +164,12 @@ class ComponentsPreview extends StatelessWidget { ); } - /// A tab strip with the second tab active. + /// A live tab strip; tapping a tab swaps the panel. Widget _buildTabs() { return Tabs( tabs: const ['Overview', 'Members', 'Settings'], - selectedIndex: 1, - onChanged: (_) {}, + selectedIndex: _tabIndex, + onChanged: (int i) => setState(() => _tabIndex = i), panelBuilder: (int index) => WDiv( className: 'p-4', child: WText('Panel ${index + 1}', className: 'text-fg text-sm'), @@ -170,7 +180,7 @@ class ComponentsPreview extends StatelessWidget { /// Each card variant with a title and body. Widget _buildCards() { return WDiv( - className: 'flex flex-row flex-wrap gap-4', + className: 'wrap gap-4', children: [ for (final CardVariant variant in CardVariant.values) WDiv( diff --git a/lib/preview/foundations.preview.dart b/lib/preview/foundations.preview.dart index 423503d..0c1b49a 100644 --- a/lib/preview/foundations.preview.dart +++ b/lib/preview/foundations.preview.dart @@ -70,7 +70,7 @@ class FoundationsPreview extends StatelessWidget { /// Surface swatches: a filled tile per background role. Widget _buildSurfaceSwatches() { return WDiv( - className: 'flex flex-row flex-wrap gap-3', + className: 'wrap gap-3', children: [ for (final (String name, String token) in _surfaceTokens) WDiv( @@ -89,7 +89,7 @@ class FoundationsPreview extends StatelessWidget { /// Foreground swatches: each role's text painted on its natural backdrop. Widget _buildForegroundSwatches() { return WDiv( - className: 'flex flex-row flex-wrap gap-3', + className: 'wrap gap-3', children: [ for (final (String name, String token) in _foregroundTokens) WDiv( @@ -107,7 +107,7 @@ class FoundationsPreview extends StatelessWidget { /// Border swatches: each border role around a neutral tile. Widget _buildBorderSwatches() { return WDiv( - className: 'flex flex-row flex-wrap gap-3', + className: 'wrap gap-3', children: [ for (final (String name, String token) in _borderTokens) WDiv( diff --git a/lib/preview/screen_preview_scaffold.dart b/lib/preview/screen_preview_scaffold.dart index 80511d4..a7c585d 100644 --- a/lib/preview/screen_preview_scaffold.dart +++ b/lib/preview/screen_preview_scaffold.dart @@ -1,5 +1,4 @@ import 'package:flutter/widgets.dart'; -import 'package:magic/magic.dart'; import 'preview_mock_harness.dart'; @@ -35,9 +34,6 @@ class ScreenPreviewScaffold extends StatefulWidget { } class _ScreenPreviewScaffoldState extends State { - /// The simulated phone viewport for feature-screen previews. - static const Size _phoneViewport = Size(390, 844); - bool _mounted = false; @override @@ -55,24 +51,17 @@ class _ScreenPreviewScaffoldState extends State { @override Widget build(BuildContext context) { - // Simulate a phone viewport. Wind resolves its responsive breakpoints - // (sm:/md:/lg:) from MediaQuery.size.width (wind_helpers.dart), NOT the - // local box constraints. Without this override a feature screen reads the - // full browser width, renders its DESKTOP layout, and overflows the narrow - // side-by-side pane. A phone-sized MediaQuery makes it render its MOBILE - // layout, which fits the constrained width with no RenderFlex overflow. - return MediaQuery( - data: MediaQuery.of(context).copyWith(size: _phoneViewport), - child: WDiv( - className: 'w-full max-w-[390px] mx-auto', - child: SingleChildScrollView( - // First frame: a sized placeholder so the column does not jump when - // the deferred view mounts. Subsequent frames: the real view. - child: _mounted - ? Builder(builder: widget.builder) - : const SizedBox(height: 320), - ), - ), + // Render the feature view at the catalog pane's natural width so it shows + // its REAL responsive layout (the screen reflows with the browser, exactly + // as in production). The catalog is a single full-width pane, so no + // phone-frame / MediaQuery override is imposed; a vertical scroll keeps a + // tall screen reachable. + return SingleChildScrollView( + // First frame: a sized placeholder so the column does not jump when the + // deferred view mounts. Subsequent frames: the real view. + child: _mounted + ? Builder(builder: widget.builder) + : const SizedBox(height: 320), ); } } From 6c725faed0d93539d0ff999a97c143c853a59388 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?An=C4=B1lcan=20=C3=87ak=C4=B1r?= Date: Fri, 26 Jun 2026 11:02:21 +0300 Subject: [PATCH 3/6] feat(preview): dedicated Sidebar + Bottom-nav navigation previews The app shell picks sidebar-vs-bottom-nav off the window width (wScreenIs reads MediaQuery.size), so the two layouts never show at the same time and the bottom nav was invisible at the catalog's desktop width. Add two dedicated previews that pin each layout by overriding the ambient MediaQuery width: SidebarMenu (desktop rail + user menu, full width) and BottomMenu (mobile header + bottom bar, in a phone frame). ScreenPreviewScaffold gains appDesktop/appMobile chrome modes; feature screens stay bare content. Also flex-1 the dashboard card title so it fits/wraps instead of overflowing (yellow stripe) in the narrow shell. --- lib/preview/_previews.g.dart | 12 +++ lib/preview/bottom_menu.preview.dart | 24 ++++++ lib/preview/screen_preview_scaffold.dart | 96 +++++++++++++++++++++--- lib/preview/sidebar_menu.preview.dart | 23 ++++++ lib/resources/views/dashboard_view.dart | 5 +- 5 files changed, 147 insertions(+), 13 deletions(-) create mode 100644 lib/preview/bottom_menu.preview.dart create mode 100644 lib/preview/sidebar_menu.preview.dart diff --git a/lib/preview/_previews.g.dart b/lib/preview/_previews.g.dart index fda03c5..b4dd227 100644 --- a/lib/preview/_previews.g.dart +++ b/lib/preview/_previews.g.dart @@ -4,6 +4,7 @@ // Source: *.preview.dart files discovered under the scan dir. import 'package:magic_devtools/preview.dart'; +import 'bottom_menu.preview.dart'; import 'components.preview.dart'; import 'dashboard_screen.preview.dart'; import 'foundations.preview.dart'; @@ -11,10 +12,16 @@ import 'login_screen.preview.dart'; import 'profile_screen.preview.dart'; import 'register_screen.preview.dart'; import 'settings_screen.preview.dart'; +import 'sidebar_menu.preview.dart'; import 'teams_screen.preview.dart'; List previewEntries() { return [ + PreviewEntry( + label: 'BottomMenu', + slug: 'bottom_menu', + builder: (_) => const BottomMenuPreview(), + ), PreviewEntry( label: 'Components', slug: 'components', @@ -50,6 +57,11 @@ List previewEntries() { slug: 'settings_screen', builder: (_) => const SettingsScreenPreview(), ), + PreviewEntry( + label: 'SidebarMenu', + slug: 'sidebar_menu', + builder: (_) => const SidebarMenuPreview(), + ), PreviewEntry( label: 'TeamsScreen', slug: 'teams_screen', diff --git a/lib/preview/bottom_menu.preview.dart b/lib/preview/bottom_menu.preview.dart new file mode 100644 index 0000000..bf5ae92 --- /dev/null +++ b/lib/preview/bottom_menu.preview.dart @@ -0,0 +1,24 @@ +import 'package:flutter/widgets.dart'; + +import '../resources/views/dashboard_view.dart'; +import 'preview_mock_harness.dart'; +import 'screen_preview_scaffold.dart'; + +/// Navigation preview: the authenticated app shell on its MOBILE layout, the +/// bottom navigation bar + drawer, rendered in a phone-width frame and wrapping +/// a sample screen. +class BottomMenuPreview extends StatelessWidget { + /// Creates the bottom-menu navigation preview. + const BottomMenuPreview({super.key}); + + @override + Widget build(BuildContext context) { + return const ScreenPreviewScaffold( + state: PreviewState.success, + chrome: PreviewChrome.appMobile, + builder: _build, + ); + } + + static Widget _build(BuildContext context) => const DashboardView(); +} diff --git a/lib/preview/screen_preview_scaffold.dart b/lib/preview/screen_preview_scaffold.dart index a7c585d..5e9dd65 100644 --- a/lib/preview/screen_preview_scaffold.dart +++ b/lib/preview/screen_preview_scaffold.dart @@ -1,12 +1,33 @@ import 'package:flutter/widgets.dart'; +import 'package:magic_starter/magic_starter.dart'; import 'preview_mock_harness.dart'; +/// The navigation chrome a feature-screen preview renders inside. +/// +/// The app shell (`layout.app`) decides sidebar-vs-bottom-nav off the window +/// width (`wScreenIs` reads `MediaQuery.size`), and the two layouts cannot show +/// at once. To preview each independently the shell modes OVERRIDE the ambient +/// `MediaQuery` width, so a single catalog pane can pin one or the other. +enum PreviewChrome { + /// Bare: just the view content, vertically scrollable. Used for plain + /// screen-content previews and guest screens (no app navigation). + none, + + /// The authenticated app shell pinned to the DESKTOP layout: the sidebar + /// navigation rail + header + user menu, full pane width. + appDesktop, + + /// The authenticated app shell pinned to the MOBILE layout: the bottom + /// navigation bar + drawer, rendered in a phone-width frame. + appMobile, +} + /// Shared scaffold for feature-screen previews. /// /// Installs the [PreviewMockHarness] for the requested [state] (so the wrapped -/// controller-backed view renders backend-free), simulates a phone viewport so -/// the view renders its mobile layout, and DEFERS the view mount by one frame. +/// controller-backed view renders backend-free) and DEFERS the view mount by +/// one frame. /// /// The deferral matters: the catalog builds a preview's body synchronously /// inside its own `build()`. A magic_starter feature view is controller-backed @@ -21,6 +42,7 @@ class ScreenPreviewScaffold extends StatefulWidget { super.key, required this.state, required this.builder, + this.chrome = PreviewChrome.none, }); /// The state the harness should portray (success, loading, or error). @@ -29,11 +51,27 @@ class ScreenPreviewScaffold extends StatefulWidget { /// Builds the feature view to preview. final WidgetBuilder builder; + /// Which navigation chrome to render the view inside. + final PreviewChrome chrome; + @override State createState() => _ScreenPreviewScaffoldState(); } class _ScreenPreviewScaffoldState extends State { + /// Bounded height for the app-shell modes. The app layout is a Scaffold with + /// `Expanded` regions and an anchored bottom nav, so it needs a finite + /// height; the catalog pane scrolls vertically (unbounded), so the shell gets + /// a representative viewport height and its content scrolls inside. + static const double _shellHeight = 760; + + /// Phone-frame width for [PreviewChrome.appMobile] (a common logical width). + static const double _phoneWidth = 390; + + /// Forced window width that keeps the shell on its desktop layout regardless + /// of the real browser width (>= the `lg` breakpoint). + static const double _desktopWidth = 1280; + bool _mounted = false; @override @@ -51,17 +89,51 @@ class _ScreenPreviewScaffoldState extends State { @override Widget build(BuildContext context) { - // Render the feature view at the catalog pane's natural width so it shows - // its REAL responsive layout (the screen reflows with the browser, exactly - // as in production). The catalog is a single full-width pane, so no - // phone-frame / MediaQuery override is imposed; a vertical scroll keeps a - // tall screen reachable. - return SingleChildScrollView( + if (!_mounted) { // First frame: a sized placeholder so the column does not jump when the - // deferred view mounts. Subsequent frames: the real view. - child: _mounted - ? Builder(builder: widget.builder) - : const SizedBox(height: 320), + // deferred view mounts. + return const SizedBox(height: 320); + } + + final Widget view = Builder(builder: widget.builder); + + switch (widget.chrome) { + case PreviewChrome.appDesktop: + // Sidebar layout, full pane width. The MediaQuery width is forced wide + // so `wScreenIs('lg')` stays true even on a narrow browser. + return SizedBox( + height: _shellHeight, + child: _withShell(context, width: _desktopWidth, child: view), + ); + case PreviewChrome.appMobile: + // Bottom-nav layout in a centered phone frame. The MediaQuery width is + // forced narrow so `wScreenIs('lg')` is false -> bottom nav + drawer. + return Center( + child: SizedBox( + width: _phoneWidth, + height: _shellHeight, + child: _withShell(context, width: _phoneWidth, child: view), + ), + ); + case PreviewChrome.none: + // Bare content at the catalog pane's natural width; a vertical scroll + // keeps a tall screen reachable. + return SingleChildScrollView(child: view); + } + } + + /// Wraps [child] in the authenticated app shell (`layout.app`) under a + /// [MediaQuery] whose width is overridden to [width], pinning the shell's + /// responsive sidebar-vs-bottom-nav decision for this preview. + Widget _withShell( + BuildContext context, { + required double width, + required Widget child, + }) { + final MediaQueryData base = MediaQuery.of(context); + return MediaQuery( + data: base.copyWith(size: Size(width, _shellHeight)), + child: MagicStarter.view.makeLayout('layout.app', child: child), ); } } diff --git a/lib/preview/sidebar_menu.preview.dart b/lib/preview/sidebar_menu.preview.dart new file mode 100644 index 0000000..0fe52b9 --- /dev/null +++ b/lib/preview/sidebar_menu.preview.dart @@ -0,0 +1,23 @@ +import 'package:flutter/widgets.dart'; + +import '../resources/views/dashboard_view.dart'; +import 'preview_mock_harness.dart'; +import 'screen_preview_scaffold.dart'; + +/// Navigation preview: the authenticated app shell on its DESKTOP layout, the +/// sidebar navigation rail + header + user menu, wrapping a sample screen. +class SidebarMenuPreview extends StatelessWidget { + /// Creates the sidebar-menu navigation preview. + const SidebarMenuPreview({super.key}); + + @override + Widget build(BuildContext context) { + return const ScreenPreviewScaffold( + state: PreviewState.success, + chrome: PreviewChrome.appDesktop, + builder: _build, + ); + } + + static Widget _build(BuildContext context) => const DashboardView(); +} diff --git a/lib/resources/views/dashboard_view.dart b/lib/resources/views/dashboard_view.dart index ed43b0e..0486f67 100644 --- a/lib/resources/views/dashboard_view.dart +++ b/lib/resources/views/dashboard_view.dart @@ -128,9 +128,12 @@ class DashboardView extends StatelessWidget { className: 'p-2 rounded-lg bg-primary-container', child: WIcon(icon, className: 'text-lg text-fg'), ), + // flex-1 so the title takes the remaining row width and fits + // (or wraps) instead of overflowing when the card is narrow, + // e.g. inside the 3-column grid beside the app-shell sidebar. WText( title, - className: 'text-base font-semibold text-fg', + className: 'flex-1 text-base font-semibold text-fg', ), ], ), From ed29652d2f12168328ea88c40ed765afa3fe1bf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?An=C4=B1lcan=20=C3=87ak=C4=B1r?= Date: Fri, 26 Jun 2026 11:49:11 +0300 Subject: [PATCH 4/6] feat(preview): split components into per-component previews (idea-design parity) Match the idea-design catalog's one-component-per-preview model: split the lumped components.preview.dart into dedicated Badge/Button/Card/Input/ Select/Switch/Tabs preview files, each its own auto-discovered section. With the catalog now a single scrolling page, bare screen previews (chrome: none) no longer wrap their own SingleChildScrollView (a nested vertical scroll broke the outer scroll geometry and sidebar scroll-to-section); the catalog page provides the scroll. --- lib/preview/_previews.g.dart | 44 ++++- lib/preview/badge.preview.dart | 20 +++ lib/preview/button.preview.dart | 48 ++++++ lib/preview/card.preview.dart | 31 ++++ lib/preview/components.preview.dart | 200 ----------------------- lib/preview/input.preview.dart | 32 ++++ lib/preview/screen_preview_scaffold.dart | 8 +- lib/preview/select.preview.dart | 32 ++++ lib/preview/switch.preview.dart | 27 +++ lib/preview/tabs.preview.dart | 29 ++++ 10 files changed, 264 insertions(+), 207 deletions(-) create mode 100644 lib/preview/badge.preview.dart create mode 100644 lib/preview/button.preview.dart create mode 100644 lib/preview/card.preview.dart delete mode 100644 lib/preview/components.preview.dart create mode 100644 lib/preview/input.preview.dart create mode 100644 lib/preview/select.preview.dart create mode 100644 lib/preview/switch.preview.dart create mode 100644 lib/preview/tabs.preview.dart diff --git a/lib/preview/_previews.g.dart b/lib/preview/_previews.g.dart index b4dd227..1011949 100644 --- a/lib/preview/_previews.g.dart +++ b/lib/preview/_previews.g.dart @@ -4,28 +4,44 @@ // Source: *.preview.dart files discovered under the scan dir. import 'package:magic_devtools/preview.dart'; +import 'badge.preview.dart'; import 'bottom_menu.preview.dart'; -import 'components.preview.dart'; +import 'button.preview.dart'; +import 'card.preview.dart'; import 'dashboard_screen.preview.dart'; import 'foundations.preview.dart'; +import 'input.preview.dart'; import 'login_screen.preview.dart'; import 'profile_screen.preview.dart'; import 'register_screen.preview.dart'; +import 'select.preview.dart'; import 'settings_screen.preview.dart'; import 'sidebar_menu.preview.dart'; +import 'switch.preview.dart'; +import 'tabs.preview.dart'; import 'teams_screen.preview.dart'; List previewEntries() { return [ + PreviewEntry( + label: 'Badge', + slug: 'badge', + builder: (_) => const BadgePreview(), + ), PreviewEntry( label: 'BottomMenu', slug: 'bottom_menu', builder: (_) => const BottomMenuPreview(), ), PreviewEntry( - label: 'Components', - slug: 'components', - builder: (_) => const ComponentsPreview(), + label: 'Button', + slug: 'button', + builder: (_) => const ButtonPreview(), + ), + PreviewEntry( + label: 'Card', + slug: 'card', + builder: (_) => const CardPreview(), ), PreviewEntry( label: 'DashboardScreen', @@ -37,6 +53,11 @@ List previewEntries() { slug: 'foundations', builder: (_) => const FoundationsPreview(), ), + PreviewEntry( + label: 'Input', + slug: 'input', + builder: (_) => const InputPreview(), + ), PreviewEntry( label: 'LoginScreen', slug: 'login_screen', @@ -52,6 +73,11 @@ List previewEntries() { slug: 'register_screen', builder: (_) => const RegisterScreenPreview(), ), + PreviewEntry( + label: 'Select', + slug: 'select', + builder: (_) => const SelectPreview(), + ), PreviewEntry( label: 'SettingsScreen', slug: 'settings_screen', @@ -62,6 +88,16 @@ List previewEntries() { slug: 'sidebar_menu', builder: (_) => const SidebarMenuPreview(), ), + PreviewEntry( + label: 'Switch', + slug: 'switch', + builder: (_) => const SwitchPreview(), + ), + PreviewEntry( + label: 'Tabs', + slug: 'tabs', + builder: (_) => const TabsPreview(), + ), PreviewEntry( label: 'TeamsScreen', slug: 'teams_screen', diff --git a/lib/preview/badge.preview.dart b/lib/preview/badge.preview.dart new file mode 100644 index 0000000..3c70577 --- /dev/null +++ b/lib/preview/badge.preview.dart @@ -0,0 +1,20 @@ +import 'package:flutter/widgets.dart'; +import 'package:magic/magic.dart' show WDiv; +import 'package:magic_starter/magic_starter.dart' show Badge, BadgeTone; + +/// Badge preview: every tone. +class BadgePreview extends StatelessWidget { + /// Creates the badge preview. + const BadgePreview({super.key}); + + @override + Widget build(BuildContext context) { + return WDiv( + className: 'wrap items-center gap-3', + children: [ + for (final BadgeTone tone in BadgeTone.values) + Badge(tone.name, tone: tone), + ], + ); + } +} diff --git a/lib/preview/button.preview.dart b/lib/preview/button.preview.dart new file mode 100644 index 0000000..8467e2e --- /dev/null +++ b/lib/preview/button.preview.dart @@ -0,0 +1,48 @@ +import 'package:flutter/widgets.dart'; +import 'package:magic/magic.dart' show WDiv, WText; +import 'package:magic_starter/magic_starter.dart' + show Button, ButtonIntent, ButtonSize; + +/// Button preview: every intent at each size, plus the loading and disabled +/// states. +class ButtonPreview extends StatelessWidget { + /// Creates the button preview. + const ButtonPreview({super.key}); + + @override + Widget build(BuildContext context) { + return WDiv( + className: 'flex flex-col gap-3', + children: [ + for (final ButtonSize size in ButtonSize.values) + WDiv( + className: 'wrap items-center gap-3', + children: [ + for (final ButtonIntent intent in ButtonIntent.values) + Button( + intent: intent, + size: size, + onPressed: () {}, + child: WText(intent.name), + ), + ], + ), + WDiv( + className: 'wrap items-center gap-3', + children: [ + Button( + isLoading: true, + onPressed: () {}, + child: const WText('Loading'), + ), + Button( + disabled: true, + onPressed: () {}, + child: const WText('Disabled'), + ), + ], + ), + ], + ); + } +} diff --git a/lib/preview/card.preview.dart b/lib/preview/card.preview.dart new file mode 100644 index 0000000..25dca68 --- /dev/null +++ b/lib/preview/card.preview.dart @@ -0,0 +1,31 @@ +import 'package:flutter/widgets.dart'; +import 'package:magic/magic.dart' show WDiv; +import 'package:magic_starter/magic_starter.dart' + show Card, CardVariant, Typography, TypographyVariant; + +/// Card preview: each card variant with a title and body. +class CardPreview extends StatelessWidget { + /// Creates the card preview. + const CardPreview({super.key}); + + @override + Widget build(BuildContext context) { + return WDiv( + className: 'wrap gap-4', + children: [ + for (final CardVariant variant in CardVariant.values) + WDiv( + className: 'w-64', + child: Card( + title: variant.name, + variant: variant, + child: const Typography( + 'Card body content.', + variant: TypographyVariant.caption, + ), + ), + ), + ], + ); + } +} diff --git a/lib/preview/components.preview.dart b/lib/preview/components.preview.dart deleted file mode 100644 index f2fe052..0000000 --- a/lib/preview/components.preview.dart +++ /dev/null @@ -1,200 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:magic/magic.dart' show SelectOption, WDiv, WText; -import 'package:magic_starter/magic_starter.dart' - show - Badge, - BadgeTone, - Button, - ButtonIntent, - ButtonSize, - Card, - CardVariant, - Input, - InputState, - MagicFormField, - Select, - Switch, - Tabs, - Typography, - TypographyVariant; - -/// Components preview: a curated matrix of the key library components. -/// -/// Buttons/badges/inputs/cards are shown as static variant matrices; the -/// interactive controls (switch, tabs, select) are LIVE so the catalog can be -/// clicked to see real behavior. Layout rows use the `wrap` utility (Wind's -/// Wrap widget) so they reflow instead of overflowing the pane. -class ComponentsPreview extends StatefulWidget { - /// Creates the components preview. - const ComponentsPreview({super.key}); - - @override - State createState() => _ComponentsPreviewState(); -} - -class _ComponentsPreviewState extends State { - bool _switchOn = true; - int _tabIndex = 1; - String _team = 'engineering'; - - @override - Widget build(BuildContext context) { - return WDiv( - className: 'flex flex-col gap-8', - children: [ - _section('Buttons', _buildButtons()), - _section('Badges', _buildBadges()), - _section('Inputs', _buildInputs()), - _section('Switches', _buildSwitches()), - _section('Select', _buildSelect()), - _section('Tabs', _buildTabs()), - _section('Cards', _buildCards()), - ], - ); - } - - /// A labelled section wrapper. - Widget _section(String title, Widget body) { - return WDiv( - className: 'flex flex-col gap-3', - children: [ - WText(title, className: 'text-fg text-sm font-semibold uppercase'), - body, - ], - ); - } - - /// Every button intent at each size, plus the loading and disabled states. - Widget _buildButtons() { - return WDiv( - className: 'flex flex-col gap-3', - children: [ - for (final ButtonSize size in ButtonSize.values) - WDiv( - className: 'wrap items-center gap-3', - children: [ - for (final ButtonIntent intent in ButtonIntent.values) - Button( - intent: intent, - size: size, - onPressed: () {}, - child: WText(intent.name), - ), - ], - ), - WDiv( - className: 'wrap items-center gap-3', - children: [ - Button( - isLoading: true, - onPressed: () {}, - child: const WText('Loading'), - ), - Button( - disabled: true, - onPressed: () {}, - child: const WText('Disabled'), - ), - ], - ), - ], - ); - } - - /// Every badge tone. - Widget _buildBadges() { - return WDiv( - className: 'wrap items-center gap-3', - children: [ - for (final BadgeTone tone in BadgeTone.values) - Badge(tone.name, tone: tone), - ], - ); - } - - /// Normal and error input states wrapped in a form field. - Widget _buildInputs() { - return const WDiv( - className: 'flex flex-col gap-4 max-w-md', - children: [ - MagicFormField( - label: 'Email', - hint: 'We never share your email.', - child: Input(placeholder: 'ada@example.com'), - ), - MagicFormField( - label: 'Password', - error: 'Password is required.', - child: Input( - placeholder: 'Enter your password', - state: InputState.error, - ), - ), - ], - ); - } - - /// A live switch the user can toggle, plus a disabled one. - Widget _buildSwitches() { - return WDiv( - className: 'wrap items-center gap-6', - children: [ - Switch( - value: _switchOn, - onChanged: (bool v) => setState(() => _switchOn = v), - ), - const Switch(value: true, onChanged: null, disabled: true), - ], - ); - } - - /// A live single-select dropdown. - Widget _buildSelect() { - return WDiv( - className: 'max-w-xs', - child: Select( - value: _team, - onChange: (String? v) => setState(() => _team = v ?? _team), - options: const >[ - SelectOption(value: 'engineering', label: 'Engineering'), - SelectOption(value: 'design', label: 'Design'), - SelectOption(value: 'personal', label: 'Personal'), - ], - ), - ); - } - - /// A live tab strip; tapping a tab swaps the panel. - Widget _buildTabs() { - return Tabs( - tabs: const ['Overview', 'Members', 'Settings'], - selectedIndex: _tabIndex, - onChanged: (int i) => setState(() => _tabIndex = i), - panelBuilder: (int index) => WDiv( - className: 'p-4', - child: WText('Panel ${index + 1}', className: 'text-fg text-sm'), - ), - ); - } - - /// Each card variant with a title and body. - Widget _buildCards() { - return WDiv( - className: 'wrap gap-4', - children: [ - for (final CardVariant variant in CardVariant.values) - WDiv( - className: 'w-64', - child: Card( - title: variant.name, - variant: variant, - child: const Typography( - 'Card body content.', - variant: TypographyVariant.caption, - ), - ), - ), - ], - ); - } -} diff --git a/lib/preview/input.preview.dart b/lib/preview/input.preview.dart new file mode 100644 index 0000000..c51e4dd --- /dev/null +++ b/lib/preview/input.preview.dart @@ -0,0 +1,32 @@ +import 'package:flutter/widgets.dart'; +import 'package:magic/magic.dart' show WDiv; +import 'package:magic_starter/magic_starter.dart' + show Input, InputState, MagicFormField; + +/// Input preview: normal and error input states wrapped in a form field. +class InputPreview extends StatelessWidget { + /// Creates the input preview. + const InputPreview({super.key}); + + @override + Widget build(BuildContext context) { + return const WDiv( + className: 'flex flex-col gap-4 max-w-md', + children: [ + MagicFormField( + label: 'Email', + hint: 'We never share your email.', + child: Input(placeholder: 'ada@example.com'), + ), + MagicFormField( + label: 'Password', + error: 'Password is required.', + child: Input( + placeholder: 'Enter your password', + state: InputState.error, + ), + ), + ], + ); + } +} diff --git a/lib/preview/screen_preview_scaffold.dart b/lib/preview/screen_preview_scaffold.dart index 5e9dd65..e4a3f9d 100644 --- a/lib/preview/screen_preview_scaffold.dart +++ b/lib/preview/screen_preview_scaffold.dart @@ -116,9 +116,11 @@ class _ScreenPreviewScaffoldState extends State { ), ); case PreviewChrome.none: - // Bare content at the catalog pane's natural width; a vertical scroll - // keeps a tall screen reachable. - return SingleChildScrollView(child: view); + // Bare content at the catalog pane's natural width. No inner scroll + // view: the catalog page already scrolls vertically, and nesting a + // second vertical SingleChildScrollView here breaks the outer scroll + // geometry (and sidebar scroll-to-section). + return view; } } diff --git a/lib/preview/select.preview.dart b/lib/preview/select.preview.dart new file mode 100644 index 0000000..5484c00 --- /dev/null +++ b/lib/preview/select.preview.dart @@ -0,0 +1,32 @@ +import 'package:flutter/widgets.dart'; +import 'package:magic/magic.dart' show SelectOption, WDiv; +import 'package:magic_starter/magic_starter.dart' show Select; + +/// Select preview: a live single-select dropdown. +class SelectPreview extends StatefulWidget { + /// Creates the select preview. + const SelectPreview({super.key}); + + @override + State createState() => _SelectPreviewState(); +} + +class _SelectPreviewState extends State { + String _team = 'engineering'; + + @override + Widget build(BuildContext context) { + return WDiv( + className: 'max-w-xs', + child: Select( + value: _team, + onChange: (String? v) => setState(() => _team = v ?? _team), + options: const >[ + SelectOption(value: 'engineering', label: 'Engineering'), + SelectOption(value: 'design', label: 'Design'), + SelectOption(value: 'personal', label: 'Personal'), + ], + ), + ); + } +} diff --git a/lib/preview/switch.preview.dart b/lib/preview/switch.preview.dart new file mode 100644 index 0000000..785e0fe --- /dev/null +++ b/lib/preview/switch.preview.dart @@ -0,0 +1,27 @@ +import 'package:flutter/widgets.dart'; +import 'package:magic/magic.dart' show WDiv; +import 'package:magic_starter/magic_starter.dart' show Switch; + +/// Switch preview: a live switch the user can toggle, plus a disabled one. +class SwitchPreview extends StatefulWidget { + /// Creates the switch preview. + const SwitchPreview({super.key}); + + @override + State createState() => _SwitchPreviewState(); +} + +class _SwitchPreviewState extends State { + bool _on = true; + + @override + Widget build(BuildContext context) { + return WDiv( + className: 'wrap items-center gap-6', + children: [ + Switch(value: _on, onChanged: (bool v) => setState(() => _on = v)), + const Switch(value: true, onChanged: null, disabled: true), + ], + ); + } +} diff --git a/lib/preview/tabs.preview.dart b/lib/preview/tabs.preview.dart new file mode 100644 index 0000000..6e6a75c --- /dev/null +++ b/lib/preview/tabs.preview.dart @@ -0,0 +1,29 @@ +import 'package:flutter/widgets.dart'; +import 'package:magic/magic.dart' show WDiv, WText; +import 'package:magic_starter/magic_starter.dart' show Tabs; + +/// Tabs preview: a live tab strip; tapping a tab swaps the panel. +class TabsPreview extends StatefulWidget { + /// Creates the tabs preview. + const TabsPreview({super.key}); + + @override + State createState() => _TabsPreviewState(); +} + +class _TabsPreviewState extends State { + int _index = 1; + + @override + Widget build(BuildContext context) { + return Tabs( + tabs: const ['Overview', 'Members', 'Settings'], + selectedIndex: _index, + onChanged: (int i) => setState(() => _index = i), + panelBuilder: (int index) => WDiv( + className: 'p-4', + child: WText('Panel ${index + 1}', className: 'text-fg text-sm'), + ), + ); + } +} From f7d6ac8b2be24fe1b5605a3ce341ab00e4763fbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?An=C4=B1lcan=20=C3=87ak=C4=B1r?= Date: Fri, 26 Jun 2026 12:08:12 +0300 Subject: [PATCH 5/6] fix(preview): empty placeholder body for nav previews SidebarMenu/BottomMenu previews showcase the navigation shell, so their body is now an empty placeholder card instead of the full DashboardView. The dashboard's 3-column cards crammed into the narrow content area beside the sidebar and broke titles mid-word ("Docum entatio n"); a neutral "Page content" card keeps the focus on the nav chrome. --- lib/preview/bottom_menu.preview.dart | 19 +++++++++++++++---- lib/preview/sidebar_menu.preview.dart | 18 +++++++++++++++--- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/lib/preview/bottom_menu.preview.dart b/lib/preview/bottom_menu.preview.dart index bf5ae92..a1f624b 100644 --- a/lib/preview/bottom_menu.preview.dart +++ b/lib/preview/bottom_menu.preview.dart @@ -1,12 +1,13 @@ import 'package:flutter/widgets.dart'; +import 'package:magic/magic.dart' show WDiv, WText; -import '../resources/views/dashboard_view.dart'; import 'preview_mock_harness.dart'; import 'screen_preview_scaffold.dart'; /// Navigation preview: the authenticated app shell on its MOBILE layout, the -/// bottom navigation bar + drawer, rendered in a phone-width frame and wrapping -/// a sample screen. +/// bottom navigation bar + drawer, rendered in a phone-width frame. The body is +/// an empty placeholder card so the preview focuses on the navigation chrome, +/// not a crammed screen. class BottomMenuPreview extends StatelessWidget { /// Creates the bottom-menu navigation preview. const BottomMenuPreview({super.key}); @@ -20,5 +21,15 @@ class BottomMenuPreview extends StatelessWidget { ); } - static Widget _build(BuildContext context) => const DashboardView(); + static Widget _build(BuildContext context) => const WDiv( + className: 'p-6', + child: WDiv( + className: ''' + w-full h-80 rounded-xl + border border-color-border bg-surface-container + flex items-center justify-center + ''', + child: WText('Page content', className: 'text-fg-muted text-sm'), + ), + ); } diff --git a/lib/preview/sidebar_menu.preview.dart b/lib/preview/sidebar_menu.preview.dart index 0fe52b9..4986746 100644 --- a/lib/preview/sidebar_menu.preview.dart +++ b/lib/preview/sidebar_menu.preview.dart @@ -1,11 +1,13 @@ import 'package:flutter/widgets.dart'; +import 'package:magic/magic.dart' show WDiv, WText; -import '../resources/views/dashboard_view.dart'; import 'preview_mock_harness.dart'; import 'screen_preview_scaffold.dart'; /// Navigation preview: the authenticated app shell on its DESKTOP layout, the -/// sidebar navigation rail + header + user menu, wrapping a sample screen. +/// sidebar navigation rail + header + user menu. The body is an empty +/// placeholder card so the preview focuses on the navigation chrome, not a +/// crammed screen. class SidebarMenuPreview extends StatelessWidget { /// Creates the sidebar-menu navigation preview. const SidebarMenuPreview({super.key}); @@ -19,5 +21,15 @@ class SidebarMenuPreview extends StatelessWidget { ); } - static Widget _build(BuildContext context) => const DashboardView(); + static Widget _build(BuildContext context) => const WDiv( + className: 'p-6', + child: WDiv( + className: ''' + w-full h-80 rounded-xl + border border-color-border bg-surface-container + flex items-center justify-center + ''', + child: WText('Page content', className: 'text-fg-muted text-sm'), + ), + ); } From 7124ec57c1a16f3f4bf7a59f13040ed41abeb31b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?An=C4=B1lcan=20=C3=87ak=C4=B1r?= Date: Fri, 26 Jun 2026 12:20:42 +0300 Subject: [PATCH 6/6] feat(preview): surface the full magic_starter component set in the catalog Register magic_starter's 30 component previews via its dev-only previews.dart barrel (User Profile Dropdown, Team Selector, Notification Dropdown, Accordion, Checkbox, Radio, Dialog, Toast, etc.), merged with the app-owned previews (foundations, screens, nav shells) and sorted by label. The 7 hand-written component preview duplicates are removed; the catalog now mirrors the whole library with no duplication. Registration stays kDebugMode-gated so the starter preview set tree-shakes from release. --- lib/app/providers/route_service_provider.dart | 13 ++++- lib/config/wind_theme.g.dart | 29 +++++------ lib/main.dart | 5 +- lib/preview/_previews.g.dart | 42 ---------------- lib/preview/badge.preview.dart | 20 -------- lib/preview/button.preview.dart | 48 ------------------- lib/preview/card.preview.dart | 31 ------------ lib/preview/input.preview.dart | 32 ------------- lib/preview/select.preview.dart | 32 ------------- lib/preview/switch.preview.dart | 27 ----------- lib/preview/tabs.preview.dart | 29 ----------- lib/resources/views/dashboard_view.dart | 10 +--- lib/resources/views/welcome_view.dart | 10 +--- 13 files changed, 30 insertions(+), 298 deletions(-) delete mode 100644 lib/preview/badge.preview.dart delete mode 100644 lib/preview/button.preview.dart delete mode 100644 lib/preview/card.preview.dart delete mode 100644 lib/preview/input.preview.dart delete mode 100644 lib/preview/select.preview.dart delete mode 100644 lib/preview/switch.preview.dart delete mode 100644 lib/preview/tabs.preview.dart diff --git a/lib/app/providers/route_service_provider.dart b/lib/app/providers/route_service_provider.dart index 983a0d1..3a264e7 100644 --- a/lib/app/providers/route_service_provider.dart +++ b/lib/app/providers/route_service_provider.dart @@ -6,6 +6,7 @@ import '../kernel.dart'; import '../../routes/app.dart'; import '../../preview/_previews.g.dart'; import 'package:magic_starter/magic_starter.dart'; +import 'package:magic_starter/previews.dart' as starter_previews; /// Route Service Provider. /// @@ -33,7 +34,17 @@ class RouteServiceProvider extends ServiceProvider { // prove the whole catalog dead in release; registerRoutes() folds itself // out behind kReleaseMode + PREVIEW_ENABLED as a second line of defence. if (kDebugMode) { - MagicPreview.register(previewEntries()); + // App-owned previews (foundations, feature screens, nav shells) plus the + // full magic_starter component set, surfaced through its dev-only + // `previews.dart` barrel (no duplication). Sorted by label for an + // idea-design-style alphabetical catalog. The whole block is kDebugMode- + // gated, so the starter preview set is tree-shaken from release. + final List entries = [ + ...previewEntries(), + for (final p in starter_previews.starterComponentPreviews()) + PreviewEntry(label: p.label, slug: p.slug, builder: p.builder), + ]..sort((a, b) => a.label.compareTo(b.label)); + MagicPreview.register(entries); MagicPreview.registerRoutes(); } } diff --git a/lib/config/wind_theme.g.dart b/lib/config/wind_theme.g.dart index 39a5e73..b186b46 100644 --- a/lib/config/wind_theme.g.dart +++ b/lib/config/wind_theme.g.dart @@ -33,20 +33,17 @@ const Map designAliases = { /// /// Seeded from the DESIGN.md `primary` light hex; consumed by /// `WindThemeData.toThemeData()` Material interop. -final Map designColors = - { - 'primary': MaterialColor( - 0xFF7C3AED, - { - 50: Color(0xFFF5EFFE), - 100: Color(0xFFEADFFC), - 200: Color(0xFFD2BCF9), - 300: Color(0xFFBB99F6), - 400: Color(0xFF9E6DF2), - 500: Color(0xFF7C3AED), - 600: Color(0xFF6D33D1), - 700: Color(0xFF5E2CB4), - 800: Color(0xFF4F2598), - 900: Color(0xFF401E7B), - }), +final Map designColors = { + 'primary': MaterialColor(0xFF7C3AED, { + 50: Color(0xFFF5EFFE), + 100: Color(0xFFEADFFC), + 200: Color(0xFFD2BCF9), + 300: Color(0xFFBB99F6), + 400: Color(0xFF9E6DF2), + 500: Color(0xFF7C3AED), + 600: Color(0xFF6D33D1), + 700: Color(0xFF5E2CB4), + 800: Color(0xFF4F2598), + 900: Color(0xFF401E7B), + }), }; diff --git a/lib/main.dart b/lib/main.dart index 61107c8..6d248e2 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -52,10 +52,7 @@ void main() async { } // Theme generated from DESIGN.md via `design:sync`. Regenerate with: // dart run magic_example:artisan design:sync - final windTheme = WindThemeData( - colors: designColors, - aliases: designAliases, - ); + final windTheme = WindThemeData(colors: designColors, aliases: designAliases); runApp(MagicApplication(title: 'Magic Example', windTheme: windTheme)); } diff --git a/lib/preview/_previews.g.dart b/lib/preview/_previews.g.dart index 1011949..f5e9730 100644 --- a/lib/preview/_previews.g.dart +++ b/lib/preview/_previews.g.dart @@ -4,45 +4,23 @@ // Source: *.preview.dart files discovered under the scan dir. import 'package:magic_devtools/preview.dart'; -import 'badge.preview.dart'; import 'bottom_menu.preview.dart'; -import 'button.preview.dart'; -import 'card.preview.dart'; import 'dashboard_screen.preview.dart'; import 'foundations.preview.dart'; -import 'input.preview.dart'; import 'login_screen.preview.dart'; import 'profile_screen.preview.dart'; import 'register_screen.preview.dart'; -import 'select.preview.dart'; import 'settings_screen.preview.dart'; import 'sidebar_menu.preview.dart'; -import 'switch.preview.dart'; -import 'tabs.preview.dart'; import 'teams_screen.preview.dart'; List previewEntries() { return [ - PreviewEntry( - label: 'Badge', - slug: 'badge', - builder: (_) => const BadgePreview(), - ), PreviewEntry( label: 'BottomMenu', slug: 'bottom_menu', builder: (_) => const BottomMenuPreview(), ), - PreviewEntry( - label: 'Button', - slug: 'button', - builder: (_) => const ButtonPreview(), - ), - PreviewEntry( - label: 'Card', - slug: 'card', - builder: (_) => const CardPreview(), - ), PreviewEntry( label: 'DashboardScreen', slug: 'dashboard_screen', @@ -53,11 +31,6 @@ List previewEntries() { slug: 'foundations', builder: (_) => const FoundationsPreview(), ), - PreviewEntry( - label: 'Input', - slug: 'input', - builder: (_) => const InputPreview(), - ), PreviewEntry( label: 'LoginScreen', slug: 'login_screen', @@ -73,11 +46,6 @@ List previewEntries() { slug: 'register_screen', builder: (_) => const RegisterScreenPreview(), ), - PreviewEntry( - label: 'Select', - slug: 'select', - builder: (_) => const SelectPreview(), - ), PreviewEntry( label: 'SettingsScreen', slug: 'settings_screen', @@ -88,16 +56,6 @@ List previewEntries() { slug: 'sidebar_menu', builder: (_) => const SidebarMenuPreview(), ), - PreviewEntry( - label: 'Switch', - slug: 'switch', - builder: (_) => const SwitchPreview(), - ), - PreviewEntry( - label: 'Tabs', - slug: 'tabs', - builder: (_) => const TabsPreview(), - ), PreviewEntry( label: 'TeamsScreen', slug: 'teams_screen', diff --git a/lib/preview/badge.preview.dart b/lib/preview/badge.preview.dart deleted file mode 100644 index 3c70577..0000000 --- a/lib/preview/badge.preview.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:magic/magic.dart' show WDiv; -import 'package:magic_starter/magic_starter.dart' show Badge, BadgeTone; - -/// Badge preview: every tone. -class BadgePreview extends StatelessWidget { - /// Creates the badge preview. - const BadgePreview({super.key}); - - @override - Widget build(BuildContext context) { - return WDiv( - className: 'wrap items-center gap-3', - children: [ - for (final BadgeTone tone in BadgeTone.values) - Badge(tone.name, tone: tone), - ], - ); - } -} diff --git a/lib/preview/button.preview.dart b/lib/preview/button.preview.dart deleted file mode 100644 index 8467e2e..0000000 --- a/lib/preview/button.preview.dart +++ /dev/null @@ -1,48 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:magic/magic.dart' show WDiv, WText; -import 'package:magic_starter/magic_starter.dart' - show Button, ButtonIntent, ButtonSize; - -/// Button preview: every intent at each size, plus the loading and disabled -/// states. -class ButtonPreview extends StatelessWidget { - /// Creates the button preview. - const ButtonPreview({super.key}); - - @override - Widget build(BuildContext context) { - return WDiv( - className: 'flex flex-col gap-3', - children: [ - for (final ButtonSize size in ButtonSize.values) - WDiv( - className: 'wrap items-center gap-3', - children: [ - for (final ButtonIntent intent in ButtonIntent.values) - Button( - intent: intent, - size: size, - onPressed: () {}, - child: WText(intent.name), - ), - ], - ), - WDiv( - className: 'wrap items-center gap-3', - children: [ - Button( - isLoading: true, - onPressed: () {}, - child: const WText('Loading'), - ), - Button( - disabled: true, - onPressed: () {}, - child: const WText('Disabled'), - ), - ], - ), - ], - ); - } -} diff --git a/lib/preview/card.preview.dart b/lib/preview/card.preview.dart deleted file mode 100644 index 25dca68..0000000 --- a/lib/preview/card.preview.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:magic/magic.dart' show WDiv; -import 'package:magic_starter/magic_starter.dart' - show Card, CardVariant, Typography, TypographyVariant; - -/// Card preview: each card variant with a title and body. -class CardPreview extends StatelessWidget { - /// Creates the card preview. - const CardPreview({super.key}); - - @override - Widget build(BuildContext context) { - return WDiv( - className: 'wrap gap-4', - children: [ - for (final CardVariant variant in CardVariant.values) - WDiv( - className: 'w-64', - child: Card( - title: variant.name, - variant: variant, - child: const Typography( - 'Card body content.', - variant: TypographyVariant.caption, - ), - ), - ), - ], - ); - } -} diff --git a/lib/preview/input.preview.dart b/lib/preview/input.preview.dart deleted file mode 100644 index c51e4dd..0000000 --- a/lib/preview/input.preview.dart +++ /dev/null @@ -1,32 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:magic/magic.dart' show WDiv; -import 'package:magic_starter/magic_starter.dart' - show Input, InputState, MagicFormField; - -/// Input preview: normal and error input states wrapped in a form field. -class InputPreview extends StatelessWidget { - /// Creates the input preview. - const InputPreview({super.key}); - - @override - Widget build(BuildContext context) { - return const WDiv( - className: 'flex flex-col gap-4 max-w-md', - children: [ - MagicFormField( - label: 'Email', - hint: 'We never share your email.', - child: Input(placeholder: 'ada@example.com'), - ), - MagicFormField( - label: 'Password', - error: 'Password is required.', - child: Input( - placeholder: 'Enter your password', - state: InputState.error, - ), - ), - ], - ); - } -} diff --git a/lib/preview/select.preview.dart b/lib/preview/select.preview.dart deleted file mode 100644 index 5484c00..0000000 --- a/lib/preview/select.preview.dart +++ /dev/null @@ -1,32 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:magic/magic.dart' show SelectOption, WDiv; -import 'package:magic_starter/magic_starter.dart' show Select; - -/// Select preview: a live single-select dropdown. -class SelectPreview extends StatefulWidget { - /// Creates the select preview. - const SelectPreview({super.key}); - - @override - State createState() => _SelectPreviewState(); -} - -class _SelectPreviewState extends State { - String _team = 'engineering'; - - @override - Widget build(BuildContext context) { - return WDiv( - className: 'max-w-xs', - child: Select( - value: _team, - onChange: (String? v) => setState(() => _team = v ?? _team), - options: const >[ - SelectOption(value: 'engineering', label: 'Engineering'), - SelectOption(value: 'design', label: 'Design'), - SelectOption(value: 'personal', label: 'Personal'), - ], - ), - ); - } -} diff --git a/lib/preview/switch.preview.dart b/lib/preview/switch.preview.dart deleted file mode 100644 index 785e0fe..0000000 --- a/lib/preview/switch.preview.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:magic/magic.dart' show WDiv; -import 'package:magic_starter/magic_starter.dart' show Switch; - -/// Switch preview: a live switch the user can toggle, plus a disabled one. -class SwitchPreview extends StatefulWidget { - /// Creates the switch preview. - const SwitchPreview({super.key}); - - @override - State createState() => _SwitchPreviewState(); -} - -class _SwitchPreviewState extends State { - bool _on = true; - - @override - Widget build(BuildContext context) { - return WDiv( - className: 'wrap items-center gap-6', - children: [ - Switch(value: _on, onChanged: (bool v) => setState(() => _on = v)), - const Switch(value: true, onChanged: null, disabled: true), - ], - ); - } -} diff --git a/lib/preview/tabs.preview.dart b/lib/preview/tabs.preview.dart deleted file mode 100644 index 6e6a75c..0000000 --- a/lib/preview/tabs.preview.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:magic/magic.dart' show WDiv, WText; -import 'package:magic_starter/magic_starter.dart' show Tabs; - -/// Tabs preview: a live tab strip; tapping a tab swaps the panel. -class TabsPreview extends StatefulWidget { - /// Creates the tabs preview. - const TabsPreview({super.key}); - - @override - State createState() => _TabsPreviewState(); -} - -class _TabsPreviewState extends State { - int _index = 1; - - @override - Widget build(BuildContext context) { - return Tabs( - tabs: const ['Overview', 'Members', 'Settings'], - selectedIndex: _index, - onChanged: (int i) => setState(() => _index = i), - panelBuilder: (int index) => WDiv( - className: 'p-4', - child: WText('Panel ${index + 1}', className: 'text-fg text-sm'), - ), - ); - } -} diff --git a/lib/resources/views/dashboard_view.dart b/lib/resources/views/dashboard_view.dart index 0486f67..0a00d79 100644 --- a/lib/resources/views/dashboard_view.dart +++ b/lib/resources/views/dashboard_view.dart @@ -131,16 +131,10 @@ class DashboardView extends StatelessWidget { // flex-1 so the title takes the remaining row width and fits // (or wraps) instead of overflowing when the card is narrow, // e.g. inside the 3-column grid beside the app-shell sidebar. - WText( - title, - className: 'flex-1 text-base font-semibold text-fg', - ), + WText(title, className: 'flex-1 text-base font-semibold text-fg'), ], ), - WText( - description, - className: 'text-sm text-fg-muted mb-3', - ), + WText(description, className: 'text-sm text-fg-muted mb-3'), WAnchor( onTap: () => Launch.url(url), child: WDiv( diff --git a/lib/resources/views/welcome_view.dart b/lib/resources/views/welcome_view.dart index c94cd9e..69d36e9 100644 --- a/lib/resources/views/welcome_view.dart +++ b/lib/resources/views/welcome_view.dart @@ -137,16 +137,10 @@ class WelcomeView extends StatelessWidget { className: 'p-2 rounded-lg bg-primary-container', child: WIcon(icon, className: 'text-lg text-fg'), ), - WText( - title, - className: 'text-base font-semibold text-fg', - ), + WText(title, className: 'text-base font-semibold text-fg'), ], ), - WText( - description, - className: 'text-sm text-fg-muted mb-3', - ), + WText(description, className: 'text-sm text-fg-muted mb-3'), WAnchor( onTap: () => Launch.url(url), child: WDiv(