@shaykec/app-agent 1.0.4 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/skills/ui-tester/SKILL.md +59 -0
- package/.cursor/skills/android-customizer/SKILL.md +50 -16
- package/.cursor/skills/appconfig-customization/SKILL.md +15 -0
- package/.cursor/skills/customization-planner/SKILL.md +17 -1
- package/.cursor/skills/ios-customizer/SKILL.md +54 -13
- package/.cursor/skills/mock-data-update/SKILL.md +11 -3
- package/.cursor/skills/ui-tester/SKILL.md +59 -0
- package/dist/cli.d.ts +18 -4
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +134 -14
- package/dist/cli.js.map +1 -1
- package/dist/github.d.ts +5 -0
- package/dist/github.d.ts.map +1 -1
- package/dist/github.js +27 -0
- package/dist/github.js.map +1 -1
- package/dist/index.js +218 -24
- package/dist/index.js.map +1 -1
- package/dist/preview-generator.d.ts +35 -0
- package/dist/preview-generator.d.ts.map +1 -0
- package/dist/preview-generator.js +546 -0
- package/dist/preview-generator.js.map +1 -0
- package/package.json +4 -1
- package/templates/android/BookTemplate/app/src/main/kotlin/com/appship/book/features/booking/BookingScreen.kt +5 -3
- package/templates/android/BookTemplate/app/src/main/kotlin/com/appship/book/features/bookings/BookingsScreen.kt +7 -5
- package/templates/android/BookTemplate/app/src/main/kotlin/com/appship/book/features/discovery/DiscoveryViewModel.kt +3 -2
- package/templates/android/BookTemplate/app/src/main/kotlin/com/appship/book/features/profile/ProfileScreen.kt +4 -2
- package/templates/android/ChatTemplate/app/src/main/kotlin/com/appship/chat/AppConfig.kt +9 -0
- package/templates/android/ChatTemplate/app/src/main/kotlin/com/appship/chat/data/DataRepository.kt +23 -0
- package/templates/android/ChatTemplate/app/src/main/kotlin/com/appship/chat/data/DataSourceResolver.kt +23 -0
- package/templates/android/ChatTemplate/app/src/main/kotlin/com/appship/chat/features/calls/CallsScreen.kt +4 -3
- package/templates/android/ChatTemplate/app/src/main/kotlin/com/appship/chat/features/contacts/ContactsScreen.kt +3 -2
- package/templates/android/ChatTemplate/app/src/main/kotlin/com/appship/chat/features/conversations/ChatScreen.kt +11 -10
- package/templates/android/ChatTemplate/app/src/main/kotlin/com/appship/chat/features/conversations/ConversationsScreen.kt +5 -5
- package/templates/android/ChatTemplate/app/src/main/kotlin/com/appship/chat/features/navigation/Navigation.kt +4 -2
- package/templates/android/ChatTemplate/app/src/main/kotlin/com/appship/chat/features/settings/SettingsScreen.kt +6 -4
- package/templates/android/ChatTemplate/app/src/main/kotlin/com/appship/chat/mock/MockDataProvider.kt +7 -7
- package/templates/android/DashTemplate/app/src/main/kotlin/com/appship/dash/data/DataRepository.kt +46 -0
- package/templates/android/DashTemplate/app/src/main/kotlin/com/appship/dash/data/DataSourceResolver.kt +23 -0
- package/templates/android/DashTemplate/app/src/main/kotlin/com/appship/dash/features/alerts/AlertsScreen.kt +4 -3
- package/templates/android/DashTemplate/app/src/main/kotlin/com/appship/dash/features/dashboard/DashboardScreen.kt +3 -2
- package/templates/android/DashTemplate/app/src/main/kotlin/com/appship/dash/features/datasources/DataSourcesScreen.kt +3 -2
- package/templates/android/DashTemplate/app/src/main/kotlin/com/appship/dash/features/reports/ReportsScreen.kt +3 -2
- package/templates/android/DashTemplate/app/src/main/kotlin/com/appship/dash/mock/MockDataProvider.kt +16 -15
- package/templates/android/FamilyTemplate/app/src/main/java/com/appship/family/AppConfig.kt +9 -0
- package/templates/android/FamilyTemplate/app/src/main/java/com/appship/family/features/calendar/CalendarScreen.kt +5 -5
- package/templates/android/FamilyTemplate/app/src/main/java/com/appship/family/features/chores/ChoreBoardScreen.kt +3 -3
- package/templates/android/FamilyTemplate/app/src/main/java/com/appship/family/features/familyhub/FamilyHubScreen.kt +4 -4
- package/templates/android/FamilyTemplate/app/src/main/java/com/appship/family/features/gallery/GalleryScreen.kt +3 -3
- package/templates/android/FamilyTemplate/app/src/main/java/com/appship/family/features/members/MembersListScreen.kt +5 -5
- package/templates/android/FamilyTemplate/app/src/main/java/com/appship/family/features/rewards/RewardsScreen.kt +5 -5
- package/templates/android/FamilyTemplate/app/src/main/java/com/appship/family/features/settings/SettingsScreen.kt +3 -3
- package/templates/android/FamilyTemplate/app/src/main/java/com/appship/family/mock/MockDataProvider.kt +29 -32
- package/templates/android/FinanceTemplate/app/src/main/kotlin/com/appship/finance/AppConfig.kt +9 -0
- package/templates/android/FinanceTemplate/app/src/main/kotlin/com/appship/finance/data/DataRepository.kt +70 -0
- package/templates/android/FinanceTemplate/app/src/main/kotlin/com/appship/finance/data/DataSourceResolver.kt +23 -0
- package/templates/android/FinanceTemplate/app/src/main/kotlin/com/appship/finance/features/budgets/BudgetsScreen.kt +3 -2
- package/templates/android/FinanceTemplate/app/src/main/kotlin/com/appship/finance/features/goals/GoalsScreen.kt +5 -3
- package/templates/android/FinanceTemplate/app/src/main/kotlin/com/appship/finance/features/home/HomeScreen.kt +8 -7
- package/templates/android/FinanceTemplate/app/src/main/kotlin/com/appship/finance/features/settings/SettingsScreen.kt +0 -1
- package/templates/android/FinanceTemplate/app/src/main/kotlin/com/appship/finance/features/transactions/TransactionsScreen.kt +9 -4
- package/templates/android/FinanceTemplate/app/src/main/kotlin/com/appship/finance/mock/MockDataProvider.kt +103 -11
- package/templates/android/GameTemplate/app/src/main/kotlin/com/appship/game/data/DataRepository.kt +19 -0
- package/templates/android/GameTemplate/app/src/main/kotlin/com/appship/game/features/leaderboard/LeaderboardScreen.kt +2 -1
- package/templates/android/GameTemplate/app/src/main/kotlin/com/appship/game/features/results/ResultsScreen.kt +3 -2
- package/templates/android/HealthTemplate/app/src/main/kotlin/com/appship/health/data/DataRepository.kt +46 -0
- package/templates/android/HealthTemplate/app/src/main/kotlin/com/appship/health/data/DataSourceResolver.kt +23 -0
- package/templates/android/HealthTemplate/app/src/main/kotlin/com/appship/health/features/dashboard/DashboardScreen.kt +3 -2
- package/templates/android/HealthTemplate/app/src/main/kotlin/com/appship/health/features/profile/ProfileScreen.kt +3 -2
- package/templates/android/HealthTemplate/app/src/main/kotlin/com/appship/health/features/workouts/WorkoutListScreen.kt +3 -2
- package/templates/android/HealthTemplate/app/src/main/kotlin/com/appship/health/mock/MockDataProvider.kt +13 -13
- package/templates/android/LearnTemplate/app/src/main/kotlin/com/appship/learn/AppConfig.kt +9 -0
- package/templates/android/LearnTemplate/app/src/main/kotlin/com/appship/learn/data/DataRepository.kt +48 -0
- package/templates/android/LearnTemplate/app/src/main/kotlin/com/appship/learn/data/DataSourceResolver.kt +23 -0
- package/templates/android/LearnTemplate/app/src/main/kotlin/com/appship/learn/features/courses/CourseListScreen.kt +4 -3
- package/templates/android/LearnTemplate/app/src/main/kotlin/com/appship/learn/features/leaderboard/LeaderboardScreen.kt +3 -2
- package/templates/android/LearnTemplate/app/src/main/kotlin/com/appship/learn/features/profile/ProfileScreen.kt +3 -2
- package/templates/android/LearnTemplate/app/src/main/kotlin/com/appship/learn/features/progress/ProgressDashboardScreen.kt +3 -2
- package/templates/android/LearnTemplate/app/src/main/kotlin/com/appship/learn/mock/MockDataProvider.kt +41 -11
- package/templates/android/MapTemplate/app/src/main/kotlin/com/appship/map/data/DataSourceResolver.kt +11 -6
- package/templates/android/MapTemplate/app/src/main/kotlin/com/appship/map/di/AppModule.kt +11 -2
- package/templates/android/MapTemplate/app/src/main/kotlin/com/appship/map/features/favorites/FavoritesViewModel.kt +4 -4
- package/templates/android/MapTemplate/app/src/main/kotlin/com/appship/map/features/map/MapViewModel.kt +4 -4
- package/templates/android/MapTemplate/app/src/main/kotlin/com/appship/map/features/place/PlaceDetailViewModel.kt +5 -5
- package/templates/android/MapTemplate/app/src/main/kotlin/com/appship/map/features/place/PlaceListViewModel.kt +4 -4
- package/templates/android/MapTemplate/app/src/main/kotlin/com/appship/map/features/profile/ProfileViewModel.kt +5 -5
- package/templates/android/MapTemplate/app/src/main/kotlin/com/appship/map/features/route/RouteViewModel.kt +3 -3
- package/templates/android/MapTemplate/app/src/main/kotlin/com/appship/map/features/search/SearchViewModel.kt +3 -3
- package/templates/android/MapTemplate/app/src/main/kotlin/com/appship/map/features/settings/SettingsViewModel.kt +3 -3
- package/templates/android/MapTemplate/app/src/main/kotlin/com/appship/map/mock/MockDataProvider.kt +54 -41
- package/templates/android/MediaTemplate/app/src/main/kotlin/com/appship/media/AppConfig.kt +9 -0
- package/templates/android/MediaTemplate/app/src/main/kotlin/com/appship/media/features/browse/BrowseScreen.kt +2 -1
- package/templates/android/MediaTemplate/app/src/main/kotlin/com/appship/media/features/detail/DetailScreen.kt +3 -1
- package/templates/android/MediaTemplate/app/src/main/kotlin/com/appship/media/features/home/HomeScreen.kt +3 -2
- package/templates/android/MediaTemplate/app/src/main/kotlin/com/appship/media/mock/MockDataProvider.kt +63 -132
- package/templates/android/ReferenceTemplate/app/src/main/kotlin/com/appship/reference/AppConfig.kt +9 -0
- package/templates/android/ShopTemplate/app/src/main/kotlin/com/appship/shop/features/home/HomeViewModel.kt +2 -1
- package/templates/android/ShopTemplate/app/src/main/kotlin/com/appship/shop/features/products/ProductViewModels.kt +12 -7
- package/templates/android/ShopTemplate/app/src/main/kotlin/com/appship/shop/mock/MockDataProvider.kt +3 -3
- package/templates/android/SocialTemplate/app/src/main/kotlin/com/appship/social/data/DataRepository.kt +0 -1
- package/templates/android/SocialTemplate/app/src/main/kotlin/com/appship/social/features/createpost/CreatePostScreen.kt +3 -2
- package/templates/android/SocialTemplate/app/src/main/kotlin/com/appship/social/features/feed/CommentsScreen.kt +4 -3
- package/templates/android/SocialTemplate/app/src/main/kotlin/com/appship/social/features/feed/FeedScreen.kt +3 -2
- package/templates/android/SocialTemplate/app/src/main/kotlin/com/appship/social/features/messages/MessagesScreen.kt +9 -8
- package/templates/android/SocialTemplate/app/src/main/kotlin/com/appship/social/features/notifications/NotificationsScreen.kt +3 -2
- package/templates/android/SocialTemplate/app/src/main/kotlin/com/appship/social/features/profile/ProfileScreen.kt +5 -4
- package/templates/android/SocialTemplate/app/src/main/kotlin/com/appship/social/features/search/SearchScreen.kt +3 -2
- package/templates/android/TaskTemplate/app/src/main/kotlin/com/appship/task/AppConfig.kt +9 -0
- package/templates/android/TaskTemplate/app/src/main/kotlin/com/appship/task/data/DataRepository.kt +38 -0
- package/templates/android/TaskTemplate/app/src/main/kotlin/com/appship/task/data/DataSourceResolver.kt +23 -0
- package/templates/android/TaskTemplate/app/src/main/kotlin/com/appship/task/features/calendar/CalendarScreen.kt +0 -1
- package/templates/android/TaskTemplate/app/src/main/kotlin/com/appship/task/features/inbox/InboxScreen.kt +4 -2
- package/templates/android/TaskTemplate/app/src/main/kotlin/com/appship/task/features/kanban/KanbanBoardScreen.kt +5 -3
- package/templates/android/TaskTemplate/app/src/main/kotlin/com/appship/task/features/projects/ProjectDetailScreen.kt +9 -5
- package/templates/android/TaskTemplate/app/src/main/kotlin/com/appship/task/features/projects/ProjectListScreen.kt +6 -4
- package/templates/android/TaskTemplate/app/src/main/kotlin/com/appship/task/features/search/SearchScreen.kt +1 -1
- package/templates/android/TaskTemplate/app/src/main/kotlin/com/appship/task/features/tasks/TaskDetailScreen.kt +0 -1
- package/templates/android/TaskTemplate/app/src/main/kotlin/com/appship/task/mock/MockDataProvider.kt +13 -12
- package/templates/android/TrackTemplate/app/src/main/kotlin/com/appship/track/AppConfig.kt +9 -0
- package/templates/android/TrackTemplate/app/src/main/kotlin/com/appship/track/data/DataRepository.kt +34 -0
- package/templates/android/TrackTemplate/app/src/main/kotlin/com/appship/track/data/DataSourceResolver.kt +23 -0
- package/templates/android/TrackTemplate/app/src/main/kotlin/com/appship/track/features/achievements/AchievementsViewModel.kt +4 -2
- package/templates/android/TrackTemplate/app/src/main/kotlin/com/appship/track/features/dashboard/TodayViewModel.kt +4 -2
- package/templates/android/TrackTemplate/app/src/main/kotlin/com/appship/track/features/habits/HabitDetailViewModel.kt +4 -2
- package/templates/android/TrackTemplate/app/src/main/kotlin/com/appship/track/features/habits/HabitsListViewModel.kt +4 -2
- package/templates/android/TrackTemplate/app/src/main/kotlin/com/appship/track/mock/MockDataProvider.kt +13 -11
- package/templates/ios/ChatTemplate/ChatTemplate/App/AppConfig.swift +9 -0
- package/templates/ios/ChatTemplate/ChatTemplate/Data/DataRepository.swift +43 -0
- package/templates/ios/ChatTemplate/ChatTemplate/Data/DataSourceResolver.swift +20 -0
- package/templates/ios/DashTemplate/DashTemplate/App/AppConfig.swift +9 -0
- package/templates/ios/DashTemplate/DashTemplate/Data/DataRepository.swift +53 -0
- package/templates/ios/DashTemplate/DashTemplate/MockData/MockDataProvider.swift +1 -1
- package/templates/ios/FamilyTemplate/FamilyTemplate/App/AppConfig.swift +9 -0
- package/templates/ios/FamilyTemplate/FamilyTemplate/App/FamilyTemplateApp.swift +4 -2
- package/templates/ios/FamilyTemplate/FamilyTemplate/MockData/MockDataProvider.swift +1 -1
- package/templates/ios/FinanceTemplate/FinanceTemplate/App/AppConfig.swift +9 -0
- package/templates/ios/FinanceTemplate/FinanceTemplate/Data/DataRepository.swift +75 -0
- package/templates/ios/FinanceTemplate/FinanceTemplate/Data/DataSourceResolver.swift +20 -0
- package/templates/ios/FinanceTemplate/FinanceTemplate/Features/Budgets/BudgetsView.swift +3 -1
- package/templates/ios/FinanceTemplate/FinanceTemplate/Features/Goals/GoalsView.swift +3 -1
- package/templates/ios/FinanceTemplate/FinanceTemplate/Features/Home/HomeView.swift +13 -6
- package/templates/ios/FinanceTemplate/FinanceTemplate/Features/Settings/SettingsView.swift +6 -2
- package/templates/ios/FinanceTemplate/FinanceTemplate/Features/Transactions/AddTransactionView.swift +4 -2
- package/templates/ios/FinanceTemplate/FinanceTemplate/Features/Transactions/TransactionsView.swift +3 -1
- package/templates/ios/FinanceTemplate/FinanceTemplate/MockData/MockDataProvider.swift +1 -1
- package/templates/ios/GameTemplate/GameTemplate/App/AppConfig.swift +9 -0
- package/templates/ios/GameTemplate/GameTemplate/Data/DataRepository.swift +30 -0
- package/templates/ios/GameTemplate/GameTemplate/Data/DataSourceResolver.swift +20 -0
- package/templates/ios/GameTemplate/GameTemplate/MockData/MockDataProvider.swift +1 -1
- package/templates/ios/HealthTemplate/HealthTemplate/App/AppConfig.swift +9 -0
- package/templates/ios/HealthTemplate/HealthTemplate/Data/DataRepository.swift +60 -0
- package/templates/ios/HealthTemplate/HealthTemplate/MockData/MockDataProvider.swift +1 -1
- package/templates/ios/LearnTemplate/LearnTemplate/App/AppConfig.swift +9 -0
- package/templates/ios/LearnTemplate/LearnTemplate/Features/Courses/CourseDetailView.swift +3 -2
- package/templates/ios/LearnTemplate/LearnTemplate/Features/Courses/CourseListView.swift +3 -2
- package/templates/ios/LearnTemplate/LearnTemplate/Features/Flashcards/FlashcardView.swift +2 -1
- package/templates/ios/LearnTemplate/LearnTemplate/Features/Leaderboard/LeaderboardView.swift +2 -1
- package/templates/ios/LearnTemplate/LearnTemplate/Features/Lessons/LessonPlayerView.swift +4 -2
- package/templates/ios/LearnTemplate/LearnTemplate/Features/Profile/ProfileView.swift +4 -2
- package/templates/ios/LearnTemplate/LearnTemplate/Features/Progress/ProgressDashboardView.swift +3 -2
- package/templates/ios/LearnTemplate/LearnTemplate/Features/Quiz/QuizView.swift +2 -1
- package/templates/ios/LearnTemplate/LearnTemplate/MockData/MockDataProvider.swift +1 -1
- package/templates/ios/MediaTemplate/MediaTemplate/App/AppConfig.swift +9 -0
- package/templates/ios/MediaTemplate/MediaTemplate/Data/DataRepository.swift +52 -0
- package/templates/ios/MediaTemplate/MediaTemplate/Data/DataSourceResolver.swift +20 -0
- package/templates/ios/MediaTemplate/MediaTemplate/MockData/MockDataProvider.swift +1 -1
- package/templates/ios/ReferenceTemplate/ReferenceTemplate/App/AppConfig.swift +9 -0
- package/templates/ios/ReferenceTemplate/ReferenceTemplate/Data/DataRepository.swift +28 -0
- package/templates/ios/ReferenceTemplate/ReferenceTemplate/Data/DataSourceResolver.swift +20 -0
- package/templates/ios/ReferenceTemplate/ReferenceTemplate/MockData/MockDataProvider.swift +1 -1
- package/templates/ios/TaskTemplate/TaskTemplate/App/AppConfig.swift +9 -0
- package/templates/ios/TaskTemplate/TaskTemplate/Data/DataRepository.swift +38 -0
- package/templates/ios/TaskTemplate/TaskTemplate/Data/DataSourceResolver.swift +20 -0
- package/templates/ios/TaskTemplate/TaskTemplate/Features/Calendar/CalendarView.swift +6 -2
- package/templates/ios/TaskTemplate/TaskTemplate/Features/Inbox/InboxView.swift +2 -1
- package/templates/ios/TaskTemplate/TaskTemplate/Features/Kanban/KanbanBoardView.swift +9 -3
- package/templates/ios/TaskTemplate/TaskTemplate/Features/Projects/ProjectDetailView.swift +3 -1
- package/templates/ios/TaskTemplate/TaskTemplate/Features/Projects/ProjectListView.swift +3 -1
- package/templates/ios/TaskTemplate/TaskTemplate/Features/Search/SearchView.swift +3 -1
- package/templates/ios/TaskTemplate/TaskTemplate/MockData/MockDataProvider.swift +1 -1
- package/templates/ios/TrackTemplate/TrackTemplate/App/AppConfig.swift +9 -0
- package/templates/ios/TrackTemplate/TrackTemplate/Data/DataRepository.swift +29 -0
- package/templates/ios/TrackTemplate/TrackTemplate/Data/DataSourceResolver.swift +20 -0
- package/templates/ios/TrackTemplate/TrackTemplate/MockData/MockDataProvider.swift +1 -1
- package/templates/ios/TrackTemplate/TrackTemplate/Services/HabitManager.swift +1 -1
|
@@ -432,6 +432,65 @@ Update `output/{app-name}/reports/summary.json` — read the file, append this s
|
|
|
432
432
|
- Set `"filesWritten"` to the total count of files created/modified
|
|
433
433
|
- Set `"duration"` to the total elapsed time
|
|
434
434
|
|
|
435
|
+
## Preview Capture
|
|
436
|
+
|
|
437
|
+
During testing, save screenshots and element tree data for the web preview feature. This enables `--preview <app-name>` to generate a clickable prototype.
|
|
438
|
+
|
|
439
|
+
### When to Capture
|
|
440
|
+
|
|
441
|
+
After inspecting each screen (in Phase 1, Step 2), save the screenshot and extract interactive element bounding boxes.
|
|
442
|
+
|
|
443
|
+
### How to Capture
|
|
444
|
+
|
|
445
|
+
1. **Create the preview directory** if it doesn't exist: `output/{app-name}/preview/`
|
|
446
|
+
|
|
447
|
+
2. **For each screen you inspect**, save the screenshot:
|
|
448
|
+
- Use `inspect()` as normal — it returns a screenshot and element tree
|
|
449
|
+
- Save the screenshot image to `output/{app-name}/preview/{screen-id}.png`
|
|
450
|
+
- `screen-id` should be a kebab-case identifier: `home`, `explore`, `item-detail`, `settings`, `profile`, etc.
|
|
451
|
+
- From the element tree, extract bounding boxes of interactive elements (buttons, list items, tabs, etc.)
|
|
452
|
+
- Record which screen each interactive element navigates to (based on what you observed during testing)
|
|
453
|
+
|
|
454
|
+
3. **After all screens are tested**, write `output/{app-name}/preview/screens.json`:
|
|
455
|
+
|
|
456
|
+
```json
|
|
457
|
+
{
|
|
458
|
+
"appName": "{App Display Name}",
|
|
459
|
+
"platform": "{ios|android}",
|
|
460
|
+
"template": "{template name if known}",
|
|
461
|
+
"designBrand": "{brand name if known}",
|
|
462
|
+
"screens": [
|
|
463
|
+
{
|
|
464
|
+
"id": "{screen-id}",
|
|
465
|
+
"label": "{Human Readable Screen Name}",
|
|
466
|
+
"screenshot": "{screen-id}.png",
|
|
467
|
+
"hotspots": [
|
|
468
|
+
{
|
|
469
|
+
"x": 0, "y": 0, "w": 0, "h": 0,
|
|
470
|
+
"target": "{target-screen-id}",
|
|
471
|
+
"label": "{Element Label}"
|
|
472
|
+
}
|
|
473
|
+
]
|
|
474
|
+
}
|
|
475
|
+
],
|
|
476
|
+
"initialScreen": "{first-screen-id}"
|
|
477
|
+
}
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
### Hotspot Rules
|
|
481
|
+
|
|
482
|
+
- `x`, `y`, `w`, `h` are bounding box coordinates in screenshot pixel space
|
|
483
|
+
- `target` is the `id` of the screen that element navigates to (must match a screen in the `screens` array)
|
|
484
|
+
- Only include hotspots for elements that navigate to another screen in the app (tabs, list items, back buttons)
|
|
485
|
+
- Omit hotspots for elements that don't navigate (toggles, text fields, action sheets)
|
|
486
|
+
- If you don't know the target screen for an element, omit that hotspot
|
|
487
|
+
|
|
488
|
+
### Important
|
|
489
|
+
|
|
490
|
+
- This capture is **in addition to** normal testing — do NOT skip any testing steps
|
|
491
|
+
- The preview directory and files are only used for the web preview feature
|
|
492
|
+
- If preview capture fails for any reason, log a warning but do NOT fail the test
|
|
493
|
+
|
|
435
494
|
## Important Rules
|
|
436
495
|
|
|
437
496
|
- ONLY use ai-tester MCP tools for device interaction — NOT shell commands like `xcrun simctl`
|
|
@@ -144,19 +144,26 @@ Templates now include an **offline-first data layer**. Understand these componen
|
|
|
144
144
|
- `reset()` function references correct defaults
|
|
145
145
|
- `data/SyncManager.kt` — if entity types were renamed, update domain methods
|
|
146
146
|
|
|
147
|
-
**ViewModel
|
|
147
|
+
**Screen/ViewModel patterns** (how screens consume data):
|
|
148
|
+
|
|
149
|
+
Screens and ViewModels use the `DataRepository` interface abstraction, not `MockDataProvider` directly:
|
|
148
150
|
```kotlin
|
|
149
|
-
// Screens
|
|
150
|
-
val
|
|
151
|
+
// Screens get the repository from DataSourceResolver
|
|
152
|
+
val repository = remember { DataSourceResolver.repository }
|
|
153
|
+
val items by repository.items.collectAsState()
|
|
154
|
+
val categories by repository.categories.collectAsState()
|
|
151
155
|
|
|
152
|
-
//
|
|
153
|
-
|
|
156
|
+
// CRUD via the repository
|
|
157
|
+
repository.addItem(newItem)
|
|
158
|
+
repository.deleteItem(item)
|
|
159
|
+
repository.toggleFavorite(item)
|
|
154
160
|
|
|
155
|
-
//
|
|
161
|
+
// If SyncManager exists, it wraps the repository
|
|
156
162
|
SyncManager.addProduct(newProduct)
|
|
157
|
-
SyncManager.deleteProduct(id)
|
|
158
163
|
```
|
|
159
164
|
|
|
165
|
+
When customizing screens or ViewModels, ALWAYS use `DataSourceResolver.repository`, never `MockDataProvider` directly. This ensures the app works with any data source (mock, localStorage, Firebase, Supabase, custom API).
|
|
166
|
+
|
|
160
167
|
**OfflineBanner integration** — add to main navigation:
|
|
161
168
|
```kotlin
|
|
162
169
|
Column {
|
|
@@ -176,26 +183,53 @@ class MyApplication : Application() {
|
|
|
176
183
|
}
|
|
177
184
|
```
|
|
178
185
|
|
|
179
|
-
### Step 7 —
|
|
186
|
+
### Step 7 — Data Layer Verification
|
|
187
|
+
|
|
188
|
+
Verify the data layer uses the correct patterns:
|
|
180
189
|
|
|
181
|
-
|
|
190
|
+
**DataRepository interface:**
|
|
191
|
+
```kotlin
|
|
192
|
+
interface DataRepository {
|
|
193
|
+
val items: StateFlow<List<Item>> // Domain entities
|
|
194
|
+
val categories: StateFlow<List<Category>>
|
|
195
|
+
val favoriteItems: StateFlow<List<Item>>
|
|
196
|
+
fun loadData()
|
|
197
|
+
fun addItem(item: Item)
|
|
198
|
+
// ... all CRUD + query methods
|
|
199
|
+
}
|
|
200
|
+
```
|
|
182
201
|
|
|
202
|
+
**MockDataProvider (implements DataRepository):**
|
|
183
203
|
```kotlin
|
|
184
|
-
object MockDataProvider {
|
|
204
|
+
object MockDataProvider : DataRepository {
|
|
185
205
|
private val _products = MutableStateFlow(defaultProducts)
|
|
186
|
-
val products: StateFlow<List<Product>> = _products.asStateFlow()
|
|
206
|
+
override val products: StateFlow<List<Product>> = _products.asStateFlow()
|
|
187
207
|
|
|
188
208
|
val defaultProducts = listOf(...) // Seed data
|
|
189
209
|
|
|
190
|
-
fun addProduct(product: Product) { ... } // CRUD
|
|
210
|
+
override fun addProduct(product: Product) { ... } // CRUD
|
|
191
211
|
fun reset() { ... } // Reset to defaults
|
|
192
212
|
}
|
|
193
213
|
```
|
|
194
214
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
215
|
+
**DataSourceResolver:**
|
|
216
|
+
```kotlin
|
|
217
|
+
object DataSourceResolver {
|
|
218
|
+
val repository: DataRepository
|
|
219
|
+
get() = when (AppConfig.DataSource.active) {
|
|
220
|
+
AppConfig.DataSource.SourceType.LOCAL_STORAGE -> MockDataProvider // TODO: LocalStorageProvider
|
|
221
|
+
AppConfig.DataSource.SourceType.MOCK -> MockDataProvider
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Verify:
|
|
227
|
+
- DataRepository interface matches MockDataProvider's public members
|
|
228
|
+
- MockDataProvider implements DataRepository with `override` on all members
|
|
229
|
+
- DataSourceResolver returns the correct implementation
|
|
230
|
+
- Screens use `DataSourceResolver.repository`, not `MockDataProvider` directly
|
|
231
|
+
- Seed data matches the new domain
|
|
232
|
+
- CRUD functions reference correct entity types
|
|
199
233
|
|
|
200
234
|
## Report Output
|
|
201
235
|
|
|
@@ -67,6 +67,21 @@ Work through each category in order:
|
|
|
67
67
|
- If the design-system skill has not run yet, choose colours appropriate for the app's domain
|
|
68
68
|
- Dark mode support — keep enabled (dark mode is handled by platform theme files)
|
|
69
69
|
|
|
70
|
+
#### CUSTOMIZE:DATASOURCE
|
|
71
|
+
- `DataSource.active` — set the active data source type
|
|
72
|
+
- Default is `.mock` during initial customization (so screens show populated data for testing)
|
|
73
|
+
- Set to `.localStorage` for the final shipped app (so users start with empty state)
|
|
74
|
+
- Other options (`.firebase`, `.supabase`, `.custom`) require BaaS shared modules to be integrated
|
|
75
|
+
- Example:
|
|
76
|
+
```swift
|
|
77
|
+
// iOS
|
|
78
|
+
static let active: SourceType = .mock // Use .localStorage for production
|
|
79
|
+
```
|
|
80
|
+
```kotlin
|
|
81
|
+
// Android
|
|
82
|
+
val active: SourceType = SourceType.MOCK // Use LOCAL_STORAGE for production
|
|
83
|
+
```
|
|
84
|
+
|
|
70
85
|
#### CUSTOMIZE:CATEGORIES (if applicable)
|
|
71
86
|
- Update category lists to match the app's domain
|
|
72
87
|
- E.g., for a pet grooming booking app: "Grooming", "Bath", "Nail Trim", "Full Package"
|
|
@@ -144,7 +144,23 @@ Based on the app domain and the existing MockDataProvider structure:
|
|
|
144
144
|
- **Categories:** List of 6-8 domain categories with names and icons
|
|
145
145
|
- **Sample items:** For each category, 3-5 items with realistic names, descriptions, and values
|
|
146
146
|
- Keep the same item counts as the template
|
|
147
|
-
- Note any entity type renames needed for SyncManager
|
|
147
|
+
- Note any entity type renames needed for DataRepository protocol, DataSourceResolver, and SyncManager
|
|
148
|
+
- **Data source:** Default data source is `localStorage` (Core Data / Room). Mock data is for testing/dev.
|
|
149
|
+
|
|
150
|
+
### Step 5b — Produce Data Repository Spec
|
|
151
|
+
|
|
152
|
+
Document how the DataRepository protocol needs to be updated for the new domain:
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
DATA REPOSITORY:
|
|
156
|
+
Entity renames:
|
|
157
|
+
- Item → {DomainEntity} (e.g., Item → Recipe)
|
|
158
|
+
- Category → {DomainCategory} (e.g., Category → CuisineType)
|
|
159
|
+
Protocol updates:
|
|
160
|
+
- Property renames: items → {domainPlural} (e.g., items → recipes)
|
|
161
|
+
- Method renames: addItem → add{Entity} (e.g., addItem → addRecipe)
|
|
162
|
+
DataSource default: .mock (agent sets .mock during customization; switch to .localStorage for shipped app)
|
|
163
|
+
```
|
|
148
164
|
|
|
149
165
|
### Step 6 — Produce Module Integration Plan
|
|
150
166
|
|
|
@@ -137,18 +137,31 @@ Templates now include an **offline-first data layer**. Understand these componen
|
|
|
137
137
|
- `Data/SyncManager.swift` — if entity types were renamed, update domain methods
|
|
138
138
|
|
|
139
139
|
**ViewModel patterns** (how views consume data):
|
|
140
|
+
|
|
141
|
+
ViewModels use the `DataRepository` protocol abstraction, not `MockDataProvider` directly:
|
|
140
142
|
```swift
|
|
141
|
-
// ViewModels
|
|
142
|
-
|
|
143
|
+
// ViewModels depend on the DataRepository protocol
|
|
144
|
+
private let repository: any DataRepository
|
|
145
|
+
|
|
146
|
+
init(repository: any DataRepository = DataSourceResolver.repository) {
|
|
147
|
+
self.repository = repository
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Read data via the protocol
|
|
151
|
+
let items = repository.items
|
|
152
|
+
let categories = repository.categories
|
|
143
153
|
|
|
144
|
-
//
|
|
145
|
-
|
|
154
|
+
// CRUD via the protocol
|
|
155
|
+
repository.addItem(newItem)
|
|
156
|
+
repository.deleteItem(item)
|
|
157
|
+
repository.toggleFavorite(item)
|
|
146
158
|
|
|
147
|
-
//
|
|
159
|
+
// If SyncManager exists, it wraps the repository
|
|
148
160
|
SyncManager.shared.addProduct(newProduct)
|
|
149
|
-
SyncManager.shared.deleteProduct(id)
|
|
150
161
|
```
|
|
151
162
|
|
|
163
|
+
When customizing ViewModels, ALWAYS use `repository` (from `DataSourceResolver`), never `MockDataProvider.shared` directly. This ensures the app works with any data source (mock, localStorage, Firebase, Supabase, custom API).
|
|
164
|
+
|
|
152
165
|
**OfflineBanner integration** — add to main navigation views:
|
|
153
166
|
```swift
|
|
154
167
|
VStack(spacing: 0) {
|
|
@@ -157,12 +170,25 @@ VStack(spacing: 0) {
|
|
|
157
170
|
}
|
|
158
171
|
```
|
|
159
172
|
|
|
160
|
-
### Step 6 —
|
|
173
|
+
### Step 6 — Data Layer Verification
|
|
174
|
+
|
|
175
|
+
Verify the data layer uses the correct patterns:
|
|
161
176
|
|
|
162
|
-
|
|
177
|
+
**DataRepository protocol:**
|
|
178
|
+
```swift
|
|
179
|
+
protocol DataRepository: AnyObject {
|
|
180
|
+
var items: [Item] { get } // Domain entities
|
|
181
|
+
var categories: [Category] { get }
|
|
182
|
+
var favoriteItems: [Item] { get }
|
|
183
|
+
func loadData()
|
|
184
|
+
func addItem(_ item: Item)
|
|
185
|
+
// ... all CRUD + query methods
|
|
186
|
+
}
|
|
187
|
+
```
|
|
163
188
|
|
|
189
|
+
**MockDataProvider (conforms to DataRepository):**
|
|
164
190
|
```swift
|
|
165
|
-
class MockDataProvider: ObservableObject {
|
|
191
|
+
class MockDataProvider: ObservableObject, DataRepository {
|
|
166
192
|
static let shared = MockDataProvider()
|
|
167
193
|
|
|
168
194
|
@Published var products: [Product] // Reactive, mutable
|
|
@@ -174,10 +200,25 @@ class MockDataProvider: ObservableObject {
|
|
|
174
200
|
}
|
|
175
201
|
```
|
|
176
202
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
203
|
+
**DataSourceResolver:**
|
|
204
|
+
```swift
|
|
205
|
+
enum DataSourceResolver {
|
|
206
|
+
static var repository: any DataRepository {
|
|
207
|
+
switch AppConfig.DataSource.active {
|
|
208
|
+
case .localStorage: return MockDataProvider.shared // TODO: LocalStorageProvider
|
|
209
|
+
case .mock: return MockDataProvider.shared
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Verify:
|
|
216
|
+
- DataRepository protocol matches MockDataProvider's public interface
|
|
217
|
+
- MockDataProvider conforms to DataRepository
|
|
218
|
+
- DataSourceResolver returns the correct implementation
|
|
219
|
+
- ViewModels use `DataSourceResolver.repository`, not `MockDataProvider.shared` directly
|
|
220
|
+
- Seed data matches the new domain
|
|
221
|
+
- CRUD methods reference correct entity types
|
|
181
222
|
|
|
182
223
|
## Report Output
|
|
183
224
|
|
|
@@ -27,7 +27,7 @@ Templates use an **offline-first architecture** with these components:
|
|
|
27
27
|
|
|
28
28
|
## Instructions
|
|
29
29
|
|
|
30
|
-
### Step 1 — Read the Current MockDataProvider
|
|
30
|
+
### Step 1 — Read the Current MockDataProvider and DataRepository
|
|
31
31
|
|
|
32
32
|
Read `MockDataProvider.swift` (iOS) or `MockDataProvider.kt` (Android). Understand:
|
|
33
33
|
- What data models exist (products, services, users, transactions, etc.)
|
|
@@ -38,6 +38,8 @@ Read `MockDataProvider.swift` (iOS) or `MockDataProvider.kt` (Android). Understa
|
|
|
38
38
|
- What properties each model has
|
|
39
39
|
- How the data is structured (arrays, dictionaries, nested objects)
|
|
40
40
|
|
|
41
|
+
Also read `DataRepository.swift` (iOS) or `DataRepository.kt` (Android) — the protocol/interface that MockDataProvider conforms to. You'll need to update it if entity types are renamed.
|
|
42
|
+
|
|
41
43
|
### Step 2 — Plan Domain-Specific Data
|
|
42
44
|
|
|
43
45
|
Based on the user's app description, plan realistic sample data. Examples:
|
|
@@ -80,9 +82,13 @@ For each collection:
|
|
|
80
82
|
6. **Use realistic dates** — recent dates, not 1970 or 2099
|
|
81
83
|
7. **Use realistic image placeholder names** — even if images don't exist, name them descriptively
|
|
82
84
|
|
|
83
|
-
### Step 4 — Update
|
|
85
|
+
### Step 4 — Update DataRepository Protocol and SyncManager (if entity types changed)
|
|
86
|
+
|
|
87
|
+
If the domain entity types have changed names (e.g., "product" → "drink"):
|
|
84
88
|
|
|
85
|
-
|
|
89
|
+
1. **DataRepository** — Update the `DataRepository.swift/.kt` protocol/interface to use the new entity type names. The protocol must always mirror MockDataProvider's public interface.
|
|
90
|
+
2. **DataSourceResolver** — If the protocol name changed, update the resolver's return type.
|
|
91
|
+
3. **SyncManager** — Update domain methods to match the new entity names. Only needed if entity types were renamed.
|
|
86
92
|
|
|
87
93
|
### Step 5 — Verify Consistency
|
|
88
94
|
|
|
@@ -117,6 +123,8 @@ After rewriting the mock data, update `output/{app-name}/reports/summary.json`
|
|
|
117
123
|
- **NEVER change the infrastructure files** (NetworkMonitor, LocalPersistence, OfflineBanner)
|
|
118
124
|
- **NEVER change model property names** — only change VALUES in seed data
|
|
119
125
|
- **DO update `default*` / `makeDefault*` seed data properties** — these are what you customize
|
|
126
|
+
- **DO update DataRepository** if entity types are renamed — the protocol must mirror MockDataProvider
|
|
127
|
+
- **DO update DataSourceResolver** if the protocol reference changes
|
|
120
128
|
- Keep data realistic and professional — this data shows in the UI during demos
|
|
121
129
|
- Maintain approximately the same data volume as the original template
|
|
122
130
|
- If the template uses image asset names, update them to be descriptive but don't worry about actual image files
|
|
@@ -466,6 +466,65 @@ Update `output/{app-name}/reports/summary.json` — read the file, append this s
|
|
|
466
466
|
- Set `"filesWritten"` to the total count of files created/modified
|
|
467
467
|
- Set `"duration"` to the total elapsed time
|
|
468
468
|
|
|
469
|
+
## Preview Capture
|
|
470
|
+
|
|
471
|
+
During testing, save screenshots and element tree data for the web preview feature. This enables `--preview <app-name>` to generate a clickable prototype.
|
|
472
|
+
|
|
473
|
+
### When to Capture
|
|
474
|
+
|
|
475
|
+
After inspecting each screen (in Phase 1, Step 2), save the screenshot and extract interactive element bounding boxes.
|
|
476
|
+
|
|
477
|
+
### How to Capture
|
|
478
|
+
|
|
479
|
+
1. **Create the preview directory** if it doesn't exist: `output/{app-name}/preview/`
|
|
480
|
+
|
|
481
|
+
2. **For each screen you inspect**, save the screenshot:
|
|
482
|
+
- Use `inspect()` as normal — it returns a screenshot and element tree
|
|
483
|
+
- Save the screenshot image to `output/{app-name}/preview/{screen-id}.png`
|
|
484
|
+
- `screen-id` should be a kebab-case identifier: `home`, `explore`, `item-detail`, `settings`, `profile`, etc.
|
|
485
|
+
- From the element tree, extract bounding boxes of interactive elements (buttons, list items, tabs, etc.)
|
|
486
|
+
- Record which screen each interactive element navigates to (based on what you observed during testing)
|
|
487
|
+
|
|
488
|
+
3. **After all screens are tested**, write `output/{app-name}/preview/screens.json`:
|
|
489
|
+
|
|
490
|
+
```json
|
|
491
|
+
{
|
|
492
|
+
"appName": "{App Display Name}",
|
|
493
|
+
"platform": "{ios|android}",
|
|
494
|
+
"template": "{template name if known}",
|
|
495
|
+
"designBrand": "{brand name if known}",
|
|
496
|
+
"screens": [
|
|
497
|
+
{
|
|
498
|
+
"id": "{screen-id}",
|
|
499
|
+
"label": "{Human Readable Screen Name}",
|
|
500
|
+
"screenshot": "{screen-id}.png",
|
|
501
|
+
"hotspots": [
|
|
502
|
+
{
|
|
503
|
+
"x": 0, "y": 0, "w": 0, "h": 0,
|
|
504
|
+
"target": "{target-screen-id}",
|
|
505
|
+
"label": "{Element Label}"
|
|
506
|
+
}
|
|
507
|
+
]
|
|
508
|
+
}
|
|
509
|
+
],
|
|
510
|
+
"initialScreen": "{first-screen-id}"
|
|
511
|
+
}
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
### Hotspot Rules
|
|
515
|
+
|
|
516
|
+
- `x`, `y`, `w`, `h` are bounding box coordinates in screenshot pixel space
|
|
517
|
+
- `target` is the `id` of the screen that element navigates to (must match a screen in the `screens` array)
|
|
518
|
+
- Only include hotspots for elements that navigate to another screen in the app (tabs, list items, back buttons)
|
|
519
|
+
- Omit hotspots for elements that don't navigate (toggles, text fields, action sheets)
|
|
520
|
+
- If you don't know the target screen for an element, omit that hotspot
|
|
521
|
+
|
|
522
|
+
### Important
|
|
523
|
+
|
|
524
|
+
- This capture is **in addition to** normal testing — do NOT skip any testing steps
|
|
525
|
+
- The preview directory and files are only used for the web preview feature
|
|
526
|
+
- If preview capture fails for any reason, log a warning but do NOT fail the test
|
|
527
|
+
|
|
469
528
|
## Important Rules
|
|
470
529
|
|
|
471
530
|
- ONLY use ai-tester MCP tools for device interaction — NOT shell commands like `xcrun simctl`
|
package/dist/cli.d.ts
CHANGED
|
@@ -7,18 +7,27 @@ export interface CliFlags {
|
|
|
7
7
|
list?: boolean;
|
|
8
8
|
run?: string;
|
|
9
9
|
engine?: Engine;
|
|
10
|
+
noPreview?: boolean;
|
|
10
11
|
}
|
|
11
12
|
export interface CliParseResult {
|
|
12
13
|
input: UserInput;
|
|
13
14
|
flags: CliFlags;
|
|
14
15
|
}
|
|
15
|
-
/** Result when a management command (--list, --run, --test) is detected */
|
|
16
|
+
/** Result when a management command (--list, --run, --test, --preview, --screenshot) is detected */
|
|
16
17
|
export interface ManagementCommand {
|
|
17
|
-
command: "list" | "run" | "test";
|
|
18
|
+
command: "list" | "run" | "test" | "preview" | "screenshot";
|
|
18
19
|
appName?: string;
|
|
19
20
|
engine?: Engine;
|
|
20
21
|
model?: Model;
|
|
21
22
|
}
|
|
23
|
+
/** Result from interactive mode: either a build input or a management command */
|
|
24
|
+
export type InteractiveResult = {
|
|
25
|
+
kind: "build";
|
|
26
|
+
input: UserInput;
|
|
27
|
+
} | {
|
|
28
|
+
kind: "management";
|
|
29
|
+
command: ManagementCommand;
|
|
30
|
+
};
|
|
22
31
|
/**
|
|
23
32
|
* Check if the user passed a management command (--list, --run).
|
|
24
33
|
* These don't require --description / --platform and skip the build flow entirely.
|
|
@@ -29,11 +38,16 @@ export declare function parseManagementCommand(): ManagementCommand | null;
|
|
|
29
38
|
* Returns UserInput + CliFlags if all required args are present, null otherwise.
|
|
30
39
|
*/
|
|
31
40
|
export declare function parseCliArgs(): CliParseResult | null;
|
|
41
|
+
/**
|
|
42
|
+
* Show an interactive picker for apps in output/.
|
|
43
|
+
* Scans output/ directories, reads summary.json, and presents a select menu.
|
|
44
|
+
*/
|
|
45
|
+
export declare function pickApp(): Promise<string>;
|
|
32
46
|
/**
|
|
33
47
|
* Collect user input interactively via the terminal.
|
|
34
|
-
* Returns
|
|
48
|
+
* Returns either a build input or a management command.
|
|
35
49
|
*/
|
|
36
|
-
export declare function collectUserInput(): Promise<
|
|
50
|
+
export declare function collectUserInput(): Promise<InteractiveResult>;
|
|
37
51
|
/** Display a summary of what will be built and ask for confirmation */
|
|
38
52
|
export declare function confirmInput(input: UserInput): Promise<boolean>;
|
|
39
53
|
//# sourceMappingURL=cli.d.ts.map
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAA2B,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAA2B,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAIrF,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;AAE9B,mDAAmD;AACnD,MAAM,WAAW,QAAQ;IACvB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,SAAS,CAAC;IACjB,KAAK,EAAE,QAAQ,CAAC;CACjB;AAED,oGAAoG;AACpG,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,SAAS,GAAG,YAAY,CAAC;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED,iFAAiF;AACjF,MAAM,MAAM,iBAAiB,GACzB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,SAAS,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,iBAAiB,CAAA;CAAE,CAAC;AAEvD;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,iBAAiB,GAAG,IAAI,CAyEjE;AAED;;;GAGG;AACH,wBAAgB,YAAY,IAAI,cAAc,GAAG,IAAI,CA2FpD;AAED;;;GAGG;AACH,wBAAsB,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,CA4D/C;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,iBAAiB,CAAC,CAqLnE;AAmHD,uEAAuE;AACvE,wBAAsB,YAAY,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,CAmDrE"}
|