@shaykec/app-agent 1.0.8 → 1.0.9
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/agents/android-customizer.md +9 -1
- package/.claude/agents/ios-customizer.md +9 -1
- package/.claude/agents/react-native-customizer.md +71 -0
- package/.claude/skills/android-customizer/SKILL.md +95 -23
- package/.claude/skills/ios-customizer/SKILL.md +102 -23
- package/.claude/skills/react-native-customizer/SKILL.md +85 -11
- package/.cursor/agents/android-customizer.md +15 -11
- package/.cursor/agents/ios-customizer.md +15 -10
- package/.cursor/agents/react-native-customizer.md +170 -0
- package/.cursor/mcp.json +2 -10
- package/.cursor/skills/android-customizer/SKILL.md +3 -3
- package/.cursor/skills/ios-customizer/SKILL.md +3 -3
- package/.cursor/skills/react-native-customizer/SKILL.md +69 -9
- package/package.json +1 -1
- package/templates/android/Skeleton/TESTING_MANIFEST.md +2 -1
- package/templates/android/Skeleton/app/src/main/kotlin/com/appship/skeleton/MainActivity.kt +23 -2
- package/templates/android/Skeleton/app/src/main/kotlin/com/appship/skeleton/core/theme/AppearanceManager.kt +42 -0
- package/templates/android/Skeleton/app/src/main/kotlin/com/appship/skeleton/features/profile/ProfileScreen.kt +20 -8
- package/templates/android/Skeleton/tests/03_detail_screen.yaml +2 -1
- package/templates/android/Skeleton/tests/04_favorites.yaml +2 -1
- package/templates/android/Skeleton/tests/08_full_e2e.yaml +2 -1
- package/templates/android/Skeleton/tests/09_dark_mode.yaml +50 -0
- package/templates/ios/Skeleton/tests/09_dark_mode.yaml +52 -0
- package/templates/react-native/Skeleton/TESTING_MANIFEST.md +1 -0
- package/templates/react-native/Skeleton/tests/09_dark_mode.yaml +46 -0
|
@@ -48,10 +48,18 @@ POTENTIAL ISSUES:
|
|
|
48
48
|
- {concerns needing manual review}
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
+
### Data Layer
|
|
52
|
+
- Use `DataSourceResolver.repository`, not `MockDataProvider` directly
|
|
53
|
+
- Preserve MockDataProvider CRUD functions and `reset()` method
|
|
54
|
+
- Do NOT modify NetworkMonitor, LocalPersistence, or OfflineBanner
|
|
55
|
+
|
|
51
56
|
## Rules
|
|
52
57
|
- ONLY edit files under `output/`
|
|
53
58
|
- Ensure all package declarations match directory structure
|
|
54
59
|
- Keep Hilt annotations intact (@HiltAndroidApp, @HiltViewModel, @AndroidEntryPoint)
|
|
55
60
|
- Keep build.gradle.kts syntactically valid
|
|
56
|
-
- Preserve MockDataProvider
|
|
61
|
+
- Preserve MockDataProvider CRUD functions and `reset()` method
|
|
62
|
+
- Do NOT modify NetworkMonitor, LocalPersistence, or OfflineBanner
|
|
57
63
|
- Use Material 3 components (not legacy Material 2)
|
|
64
|
+
- Preserve ALL `Modifier.testTag()` calls — do NOT remove test IDs
|
|
65
|
+
- Follow `{screen}_{element}_{role}` convention for new test tags (see `templates/TEST_ID_CONVENTIONS.md`)
|
|
@@ -46,9 +46,17 @@ POTENTIAL ISSUES:
|
|
|
46
46
|
- {concerns needing manual review}
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
+
### Data Layer
|
|
50
|
+
- Use `DataSourceResolver.repository` in ViewModels, not `MockDataProvider.shared` directly
|
|
51
|
+
- Preserve MockDataProvider CRUD methods and `reset()` function
|
|
52
|
+
- Do NOT modify NetworkMonitor, LocalPersistence, or OfflineBanner
|
|
53
|
+
|
|
49
54
|
## Rules
|
|
50
55
|
- ONLY edit files under `output/` — never touch `templates/`
|
|
51
56
|
- Ensure all imports resolve after changes
|
|
52
57
|
- Keep `@main` struct and `App` protocol conformance intact
|
|
53
|
-
- Preserve MockDataProvider function
|
|
58
|
+
- Preserve MockDataProvider CRUD methods and `reset()` function
|
|
59
|
+
- Do NOT modify NetworkMonitor, LocalPersistence, or OfflineBanner
|
|
54
60
|
- Use SF Symbols for tab and navigation icons
|
|
61
|
+
- Preserve ALL `.accessibilityIdentifier()` calls — do NOT remove test IDs
|
|
62
|
+
- Follow `{screen}_{element}_{role}` convention for new test IDs (see `templates/TEST_ID_CONVENTIONS.md`)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: react-native-customizer
|
|
3
|
+
description: Specialized React Native/TypeScript subagent for customizing React Native app templates. Handles package config updates, component rewriting, React Navigation adjustments, theme application, and cross-platform features.
|
|
4
|
+
tools: Read, Edit, Write, Grep, Glob, LS, Bash
|
|
5
|
+
model: sonnet
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
You are an expert React Native developer specializing in TypeScript and modern mobile development. You handle all React Native-specific customization tasks for the AppAgent pipeline.
|
|
9
|
+
|
|
10
|
+
## Your Expertise
|
|
11
|
+
- React Native 0.73+, TypeScript 5+, React Navigation 6+
|
|
12
|
+
- MVVM architecture with custom hooks (`useXxxViewModel`)
|
|
13
|
+
- StyleSheet.create() for performant styling
|
|
14
|
+
- FlatList / SectionList for virtualized lists
|
|
15
|
+
- React Context for theming and global state
|
|
16
|
+
|
|
17
|
+
## Tasks You Handle
|
|
18
|
+
|
|
19
|
+
### Package & Project Renaming
|
|
20
|
+
1. Update `package.json` — `name` field
|
|
21
|
+
2. Update `app.json` — `name` and `displayName`
|
|
22
|
+
3. Update `android/app/build.gradle` — `applicationId`, `namespace`
|
|
23
|
+
4. Update `android/settings.gradle` — `rootProject.name`
|
|
24
|
+
5. Update `android/app/src/main/res/values/strings.xml` — `app_name`
|
|
25
|
+
6. Update `android/app/src/main/java/` — package directories, `MainActivity.kt`, `MainApplication.kt`
|
|
26
|
+
7. Update `ios/Podfile` — project target name
|
|
27
|
+
8. Update all imports if module paths changed
|
|
28
|
+
|
|
29
|
+
### React Native Component Customization
|
|
30
|
+
- Use functional components with `React.FC` typing
|
|
31
|
+
- Use custom hooks (`useXxxViewModel`) for state and business logic
|
|
32
|
+
- Use `StyleSheet.create()` for all styles — no inline style objects
|
|
33
|
+
- Use `FlatList` / `SectionList` for lists (not `map()` on `ScrollView`)
|
|
34
|
+
- Add `testID` props to ALL interactive elements
|
|
35
|
+
|
|
36
|
+
### Navigation Structure
|
|
37
|
+
Templates use React Navigation with bottom tabs and native stack. When modifying:
|
|
38
|
+
- Update tab names and components in `AppNavigator.tsx`
|
|
39
|
+
- Update `RootStackParamList` type for type-safe navigation
|
|
40
|
+
- Apply tab labels from content brief and icons from design brief
|
|
41
|
+
|
|
42
|
+
### Theme Customization
|
|
43
|
+
- Update `ThemeContext.tsx` colors to match brand palette
|
|
44
|
+
- Provide both light and dark mode variants
|
|
45
|
+
- Apply corner radius, spacing, and typography tokens
|
|
46
|
+
|
|
47
|
+
## Output
|
|
48
|
+
```
|
|
49
|
+
FILES MODIFIED:
|
|
50
|
+
- {path}: {what changed}
|
|
51
|
+
POTENTIAL ISSUES:
|
|
52
|
+
- {concerns needing manual review}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Data Layer
|
|
56
|
+
- Use `DataSourceResolver.repository` in ViewModel hooks, not `MockDataProvider` directly
|
|
57
|
+
- Preserve MockDataProvider CRUD methods and `reset()` function
|
|
58
|
+
- Do NOT modify NetworkMonitor, LocalPersistence, or OfflineBanner
|
|
59
|
+
|
|
60
|
+
## Rules
|
|
61
|
+
- ONLY edit files under `output/` — never touch `templates/`
|
|
62
|
+
- Ensure all imports resolve after changes
|
|
63
|
+
- Preserve MockDataProvider CRUD methods and `reset()` function
|
|
64
|
+
- Do NOT modify NetworkMonitor, LocalPersistence, or OfflineBanner
|
|
65
|
+
- Ensure OfflineBanner is integrated into main navigation screens
|
|
66
|
+
- Use `testID` props on ALL interactive elements
|
|
67
|
+
- Follow `{screen}_{element}_{role}` convention for test IDs (see `templates/TEST_ID_CONVENTIONS.md`)
|
|
68
|
+
- Use `StyleSheet.create()` for styles, not inline objects
|
|
69
|
+
- Keep React Navigation structure intact
|
|
70
|
+
- Preserve ThemeContext provider wrapping in App.tsx
|
|
71
|
+
- Do NOT introduce new npm dependencies
|
|
@@ -9,7 +9,7 @@ Use this skill to perform deep Android/Kotlin/Compose customization on a cloned
|
|
|
9
9
|
|
|
10
10
|
## When to Use
|
|
11
11
|
|
|
12
|
-
- At Step
|
|
12
|
+
- At Step 5 of the AppAgent workflow, when the platform is Android
|
|
13
13
|
- After AppConfig, mock data, and design tokens are already in place
|
|
14
14
|
- When you need to modify Compose screens, navigation, and themes
|
|
15
15
|
|
|
@@ -102,6 +102,17 @@ Android templates use Material 3 dynamic theming in `Theme.kt`:
|
|
|
102
102
|
- Apply typography scale if the design brief specifies custom fonts
|
|
103
103
|
- Apply shape theme (corner radius) from the design brief
|
|
104
104
|
|
|
105
|
+
### Step 5.5 — Generate App Launcher Icon
|
|
106
|
+
|
|
107
|
+
Customize the adaptive icon using the manifest's **App Launcher Icon** section:
|
|
108
|
+
|
|
109
|
+
1. Read the icon spec: `Android vector keyword` (e.g., `shopping`) and `Background` hex color
|
|
110
|
+
2. Read `templates/android/ICON_VECTORS.md`, find the matching keyword section, copy the XML into `app/src/main/res/drawable/ic_launcher_foreground.xml`
|
|
111
|
+
3. Update `app/src/main/res/values/colors.xml`: set `ic_launcher_background` to the background hex
|
|
112
|
+
4. Verify `ic_launcher.xml` and `ic_launcher_round.xml` reference the correct foreground/background
|
|
113
|
+
|
|
114
|
+
If `ICON_VECTORS.md` does not exist or the keyword is not found, keep the default icon and only update the background color.
|
|
115
|
+
|
|
105
116
|
### Step 6 — Data Layer & Offline Infrastructure
|
|
106
117
|
|
|
107
118
|
Templates now include an **offline-first data layer**. Understand these components:
|
|
@@ -119,19 +130,26 @@ Templates now include an **offline-first data layer**. Understand these componen
|
|
|
119
130
|
- `reset()` function references correct defaults
|
|
120
131
|
- `data/SyncManager.kt` — if entity types were renamed, update domain methods
|
|
121
132
|
|
|
122
|
-
**ViewModel
|
|
133
|
+
**Screen/ViewModel patterns** (how screens consume data):
|
|
134
|
+
|
|
135
|
+
Screens and ViewModels use the `DataRepository` interface abstraction, not `MockDataProvider` directly:
|
|
123
136
|
```kotlin
|
|
124
|
-
// Screens
|
|
125
|
-
val
|
|
137
|
+
// Screens get the repository from DataSourceResolver
|
|
138
|
+
val repository = remember { DataSourceResolver.repository }
|
|
139
|
+
val items by repository.items.collectAsState()
|
|
140
|
+
val categories by repository.categories.collectAsState()
|
|
126
141
|
|
|
127
|
-
//
|
|
128
|
-
|
|
142
|
+
// CRUD via the repository
|
|
143
|
+
repository.addItem(newItem)
|
|
144
|
+
repository.deleteItem(item)
|
|
145
|
+
repository.toggleFavorite(item)
|
|
129
146
|
|
|
130
|
-
//
|
|
147
|
+
// If SyncManager exists, it wraps the repository
|
|
131
148
|
SyncManager.addProduct(newProduct)
|
|
132
|
-
SyncManager.deleteProduct(id)
|
|
133
149
|
```
|
|
134
150
|
|
|
151
|
+
When customizing screens or ViewModels, ALWAYS use `DataSourceResolver.repository`, never `MockDataProvider` directly. This ensures the app works with any data source.
|
|
152
|
+
|
|
135
153
|
**OfflineBanner integration** — add to main navigation:
|
|
136
154
|
```kotlin
|
|
137
155
|
Column {
|
|
@@ -151,26 +169,75 @@ class MyApplication : Application() {
|
|
|
151
169
|
}
|
|
152
170
|
```
|
|
153
171
|
|
|
154
|
-
### Step 7 —
|
|
172
|
+
### Step 7 — Data Layer Verification
|
|
155
173
|
|
|
156
|
-
Verify
|
|
174
|
+
Verify the data layer uses the correct patterns:
|
|
157
175
|
|
|
176
|
+
**DataRepository interface:**
|
|
158
177
|
```kotlin
|
|
159
|
-
|
|
178
|
+
interface DataRepository {
|
|
179
|
+
val items: StateFlow<List<Item>>
|
|
180
|
+
val categories: StateFlow<List<Category>>
|
|
181
|
+
val favoriteItems: StateFlow<List<Item>>
|
|
182
|
+
fun loadData()
|
|
183
|
+
fun addItem(item: Item)
|
|
184
|
+
// ... all CRUD + query methods
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
**MockDataProvider (implements DataRepository):**
|
|
189
|
+
```kotlin
|
|
190
|
+
object MockDataProvider : DataRepository {
|
|
160
191
|
private val _products = MutableStateFlow(defaultProducts)
|
|
161
|
-
val products: StateFlow<List<Product>> = _products.asStateFlow()
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
192
|
+
override val products: StateFlow<List<Product>> = _products.asStateFlow()
|
|
193
|
+
val defaultProducts = listOf(...)
|
|
194
|
+
override fun addProduct(product: Product) { ... }
|
|
195
|
+
fun reset() { ... }
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
**DataSourceResolver:**
|
|
200
|
+
```kotlin
|
|
201
|
+
object DataSourceResolver {
|
|
202
|
+
val repository: DataRepository
|
|
203
|
+
get() = when (AppConfig.DataSource.active) {
|
|
204
|
+
AppConfig.DataSource.SourceType.MOCK -> MockDataProvider
|
|
205
|
+
AppConfig.DataSource.SourceType.LOCAL_STORAGE -> MockDataProvider // TODO
|
|
206
|
+
}
|
|
167
207
|
}
|
|
168
208
|
```
|
|
169
209
|
|
|
170
|
-
|
|
171
|
-
-
|
|
172
|
-
-
|
|
173
|
-
-
|
|
210
|
+
Verify:
|
|
211
|
+
- DataRepository interface matches MockDataProvider's public members
|
|
212
|
+
- MockDataProvider implements DataRepository with `override` on all members
|
|
213
|
+
- DataSourceResolver returns the correct implementation
|
|
214
|
+
- Screens use `DataSourceResolver.repository`, not `MockDataProvider` directly
|
|
215
|
+
- Seed data matches the new domain
|
|
216
|
+
- CRUD functions reference correct entity types
|
|
217
|
+
|
|
218
|
+
### Step 7.5 — Apply Animation Modifiers (if animation modules are integrated)
|
|
219
|
+
|
|
220
|
+
If the customization manifest includes an **"Animation & Sensory Plan"** section:
|
|
221
|
+
|
|
222
|
+
1. Read the "Per-Screen Animation Modifiers" table from the manifest
|
|
223
|
+
2. For each screen in your batch, apply the assigned modifiers:
|
|
224
|
+
|
|
225
|
+
```kotlin
|
|
226
|
+
// MicroInteractions — press feedback
|
|
227
|
+
Button(onClick = { }, modifier = Modifier.scaleOnPress().hapticFeedback(HapticFeedbackStyle.IMPACT_MEDIUM))
|
|
228
|
+
|
|
229
|
+
// ScrollEffects — parallax header
|
|
230
|
+
ParallaxHeader(height = 280.dp, scrollState = scrollState) { AsyncImage(model = item.imageUrl) }
|
|
231
|
+
|
|
232
|
+
// AnimatedTransitions — staggered list items
|
|
233
|
+
ItemRow(item = item, modifier = Modifier.staggeredAppearance(index = index))
|
|
234
|
+
|
|
235
|
+
// CelebrationEffects — success overlay
|
|
236
|
+
Box(modifier = Modifier.celebrationOverlay(isActive = showCelebration, style = CelebrationStyle.CONFETTI))
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
3. Wrap all applied animations with `motionAwareAnimationSpec()` if MotionPreferences is integrated
|
|
240
|
+
4. Do NOT add animation modifiers to screens not listed in the manifest
|
|
174
241
|
|
|
175
242
|
## Report Output
|
|
176
243
|
|
|
@@ -179,7 +246,7 @@ Write the report to `output/{app-name}/reports/05-customization.md` (append Andr
|
|
|
179
246
|
```markdown
|
|
180
247
|
# Customization Report
|
|
181
248
|
|
|
182
|
-
**Step:**
|
|
249
|
+
**Step:** 5
|
|
183
250
|
**Skill:** android-customizer
|
|
184
251
|
**Timestamp:** {ISO 8601}
|
|
185
252
|
**Result:** PASS
|
|
@@ -214,6 +281,11 @@ Android (Kotlin / Jetpack Compose)
|
|
|
214
281
|
- SyncManager methods updated: {list or "no changes needed"}
|
|
215
282
|
- Data persistence verified: {yes/no}
|
|
216
283
|
|
|
284
|
+
## Animation Modifiers Applied
|
|
285
|
+
|
|
286
|
+
- {modifier}: applied to {screen} — {description}
|
|
287
|
+
- MotionPreferences respected: {yes/no/N/A}
|
|
288
|
+
|
|
217
289
|
## Potential Issues
|
|
218
290
|
|
|
219
291
|
- {any concerns or items needing manual review}
|
|
@@ -225,7 +297,7 @@ Update `output/{app-name}/reports/summary.json` — read the file, append this s
|
|
|
225
297
|
|
|
226
298
|
```json
|
|
227
299
|
{
|
|
228
|
-
"step":
|
|
300
|
+
"step": 5,
|
|
229
301
|
"name": "android-customization",
|
|
230
302
|
"startedAt": "{ISO 8601 timestamp}",
|
|
231
303
|
"durationSeconds": 0,
|
|
@@ -9,7 +9,7 @@ Use this skill to perform deep iOS/SwiftUI customization on a cloned template.
|
|
|
9
9
|
|
|
10
10
|
## When to Use
|
|
11
11
|
|
|
12
|
-
- At Step
|
|
12
|
+
- At Step 5 of the AppAgent workflow, when the platform is iOS
|
|
13
13
|
- After AppConfig, mock data, and design tokens are already in place
|
|
14
14
|
- When you need to modify SwiftUI views, navigation, and themes
|
|
15
15
|
|
|
@@ -100,6 +100,16 @@ iOS templates use a `Colors.swift` theme file:
|
|
|
100
100
|
- Use semantic colors where possible (`Color.primary`, `Color.secondary`)
|
|
101
101
|
- Apply corner radius and spacing tokens from the design brief
|
|
102
102
|
|
|
103
|
+
### Step 4.5 — Generate App Launcher Icon
|
|
104
|
+
|
|
105
|
+
Generate a domain-specific app icon using the manifest's **App Launcher Icon** section:
|
|
106
|
+
|
|
107
|
+
1. Read the icon spec: `iOS SF Symbol` (e.g., `bag.fill`) and `Background` hex color
|
|
108
|
+
2. Run: `swift scripts/generate-app-icon.swift "{symbol}" "{background}" "output/{app-name}/{AppName}/Resources/Assets.xcassets/AppIcon.appiconset/"`
|
|
109
|
+
3. Verify `AppIcon-1024.png` exists in the output directory
|
|
110
|
+
|
|
111
|
+
If `scripts/generate-app-icon.swift` does not exist or fails, skip — the template's default icon will be used.
|
|
112
|
+
|
|
103
113
|
### Step 5 — Data Layer & Offline Infrastructure
|
|
104
114
|
|
|
105
115
|
Templates now include an **offline-first data layer**. Understand these components:
|
|
@@ -118,18 +128,31 @@ Templates now include an **offline-first data layer**. Understand these componen
|
|
|
118
128
|
- `Data/SyncManager.swift` — if entity types were renamed, update domain methods
|
|
119
129
|
|
|
120
130
|
**ViewModel patterns** (how views consume data):
|
|
131
|
+
|
|
132
|
+
ViewModels use the `DataRepository` protocol abstraction, not `MockDataProvider` directly:
|
|
121
133
|
```swift
|
|
122
|
-
// ViewModels
|
|
123
|
-
|
|
134
|
+
// ViewModels depend on the DataRepository protocol
|
|
135
|
+
private let repository: any DataRepository
|
|
136
|
+
|
|
137
|
+
init(repository: any DataRepository = DataSourceResolver.repository) {
|
|
138
|
+
self.repository = repository
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Read data via the protocol
|
|
142
|
+
let items = repository.items
|
|
143
|
+
let categories = repository.categories
|
|
124
144
|
|
|
125
|
-
//
|
|
126
|
-
|
|
145
|
+
// CRUD via the protocol
|
|
146
|
+
repository.addItem(newItem)
|
|
147
|
+
repository.deleteItem(item)
|
|
148
|
+
repository.toggleFavorite(item)
|
|
127
149
|
|
|
128
|
-
//
|
|
150
|
+
// If SyncManager exists, it wraps the repository
|
|
129
151
|
SyncManager.shared.addProduct(newProduct)
|
|
130
|
-
SyncManager.shared.deleteProduct(id)
|
|
131
152
|
```
|
|
132
153
|
|
|
154
|
+
When customizing ViewModels, ALWAYS use `repository` (from `DataSourceResolver`), never `MockDataProvider.shared` directly. This ensures the app works with any data source.
|
|
155
|
+
|
|
133
156
|
**OfflineBanner integration** — add to main navigation views:
|
|
134
157
|
```swift
|
|
135
158
|
VStack(spacing: 0) {
|
|
@@ -138,27 +161,78 @@ VStack(spacing: 0) {
|
|
|
138
161
|
}
|
|
139
162
|
```
|
|
140
163
|
|
|
141
|
-
### Step 6 —
|
|
164
|
+
### Step 6 — Data Layer Verification
|
|
142
165
|
|
|
143
|
-
Verify the
|
|
166
|
+
Verify the data layer uses the correct patterns:
|
|
144
167
|
|
|
168
|
+
**DataRepository protocol:**
|
|
145
169
|
```swift
|
|
146
|
-
|
|
170
|
+
protocol DataRepository: AnyObject {
|
|
171
|
+
var items: [Item] { get }
|
|
172
|
+
var categories: [Category] { get }
|
|
173
|
+
var favoriteItems: [Item] { get }
|
|
174
|
+
func loadData()
|
|
175
|
+
func addItem(_ item: Item)
|
|
176
|
+
// ... all CRUD + query methods
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**MockDataProvider (conforms to DataRepository):**
|
|
181
|
+
```swift
|
|
182
|
+
class MockDataProvider: ObservableObject, DataRepository {
|
|
147
183
|
static let shared = MockDataProvider()
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
184
|
+
@Published var products: [Product]
|
|
185
|
+
static let defaultProducts: [Product] = [...]
|
|
186
|
+
func addProduct(_ product: Product) { ... }
|
|
187
|
+
func reset() { ... }
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
**DataSourceResolver:**
|
|
192
|
+
```swift
|
|
193
|
+
enum DataSourceResolver {
|
|
194
|
+
static var repository: any DataRepository {
|
|
195
|
+
switch AppConfig.DataSource.active {
|
|
196
|
+
case .mock: return MockDataProvider.shared
|
|
197
|
+
case .localStorage: return MockDataProvider.shared // TODO: LocalStorageProvider
|
|
198
|
+
}
|
|
199
|
+
}
|
|
155
200
|
}
|
|
156
201
|
```
|
|
157
202
|
|
|
158
|
-
|
|
159
|
-
-
|
|
160
|
-
-
|
|
161
|
-
-
|
|
203
|
+
Verify:
|
|
204
|
+
- DataRepository protocol matches MockDataProvider's public interface
|
|
205
|
+
- MockDataProvider conforms to DataRepository
|
|
206
|
+
- DataSourceResolver returns the correct implementation
|
|
207
|
+
- ViewModels use `DataSourceResolver.repository`, not `MockDataProvider.shared` directly
|
|
208
|
+
- Seed data matches the new domain
|
|
209
|
+
- CRUD methods reference correct entity types
|
|
210
|
+
|
|
211
|
+
### Step 6.5 — Apply Animation Modifiers (if animation modules are integrated)
|
|
212
|
+
|
|
213
|
+
If the customization manifest includes an **"Animation & Sensory Plan"** section:
|
|
214
|
+
|
|
215
|
+
1. Read the "Per-Screen Animation Modifiers" table from the manifest
|
|
216
|
+
2. For each screen in your batch, apply the assigned modifiers:
|
|
217
|
+
|
|
218
|
+
```swift
|
|
219
|
+
// MicroInteractions — press feedback
|
|
220
|
+
Button("Add to Cart") { ... }
|
|
221
|
+
.scaleOnPress()
|
|
222
|
+
.hapticFeedback(.impact(.medium))
|
|
223
|
+
|
|
224
|
+
// ScrollEffects — parallax header
|
|
225
|
+
ParallaxHeaderView(height: 280) { AsyncImage(url: item.imageURL) }
|
|
226
|
+
|
|
227
|
+
// AnimatedTransitions — staggered list items
|
|
228
|
+
ItemRow(item: items[index]).staggeredAppearance(index: index)
|
|
229
|
+
|
|
230
|
+
// CelebrationEffects — success overlay
|
|
231
|
+
.celebrationOverlay(isPresented: $showCelebration, style: .confetti)
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
3. Wrap all applied animations with `.motionAware()` if MotionPreferences is integrated
|
|
235
|
+
4. Do NOT add animation modifiers to screens not listed in the manifest
|
|
162
236
|
|
|
163
237
|
## Report Output
|
|
164
238
|
|
|
@@ -167,7 +241,7 @@ Write the report to `output/{app-name}/reports/05-customization.md` (append iOS
|
|
|
167
241
|
```markdown
|
|
168
242
|
# Customization Report
|
|
169
243
|
|
|
170
|
-
**Step:**
|
|
244
|
+
**Step:** 5
|
|
171
245
|
**Skill:** ios-customizer
|
|
172
246
|
**Timestamp:** {ISO 8601}
|
|
173
247
|
**Result:** PASS
|
|
@@ -201,6 +275,11 @@ iOS (Swift / SwiftUI)
|
|
|
201
275
|
- SyncManager methods updated: {list or "no changes needed"}
|
|
202
276
|
- Data persistence verified: {yes/no}
|
|
203
277
|
|
|
278
|
+
## Animation Modifiers Applied
|
|
279
|
+
|
|
280
|
+
- {modifier}: applied to {screen} — {description}
|
|
281
|
+
- MotionPreferences respected: {yes/no/N/A}
|
|
282
|
+
|
|
204
283
|
## Potential Issues
|
|
205
284
|
|
|
206
285
|
- {any concerns or items needing manual review}
|
|
@@ -212,7 +291,7 @@ Update `output/{app-name}/reports/summary.json` — read the file, append this s
|
|
|
212
291
|
|
|
213
292
|
```json
|
|
214
293
|
{
|
|
215
|
-
"step":
|
|
294
|
+
"step": 5,
|
|
216
295
|
"name": "ios-customization",
|
|
217
296
|
"startedAt": "{ISO 8601 timestamp}",
|
|
218
297
|
"durationSeconds": 0,
|
|
@@ -91,27 +91,88 @@ React Native templates use a `ThemeContext.tsx`:
|
|
|
91
91
|
- Apply corner radius and spacing tokens
|
|
92
92
|
- Apply typography settings (font sizes, weights)
|
|
93
93
|
|
|
94
|
-
### Step 5 — Data Layer &
|
|
94
|
+
### Step 5 — Data Layer & Offline Infrastructure
|
|
95
|
+
|
|
96
|
+
Templates include an **offline-first data layer**:
|
|
95
97
|
|
|
96
98
|
**Files you should NOT modify** (generic infrastructure):
|
|
97
99
|
- `src/data/DataRepository.ts` — interface definition
|
|
100
|
+
- `src/data/NetworkMonitor.ts` — connectivity detection
|
|
101
|
+
- `src/data/LocalPersistence.ts` — JSON file persistence
|
|
102
|
+
- `src/components/OfflineBanner.tsx` — offline status banner
|
|
98
103
|
- `src/theme/ThemeContext.tsx` — theme provider
|
|
99
104
|
|
|
100
105
|
**Files you MAY need to update** (domain-specific):
|
|
101
|
-
- `src/mock/MockDataProvider.ts` — seed data already updated in Step 4. Verify
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
+
- `src/mock/MockDataProvider.ts` — seed data already updated in Step 4. Verify seed data, CRUD methods, subscription pattern, and reset defaults
|
|
107
|
+
- `src/data/SyncManager.ts` — if entity types were renamed, update domain methods
|
|
108
|
+
|
|
109
|
+
**ViewModel patterns** — use `DataSourceResolver`, not `MockDataProvider` directly:
|
|
110
|
+
```tsx
|
|
111
|
+
import { DataSourceResolver } from '../data/DataSourceResolver';
|
|
112
|
+
|
|
113
|
+
export function useHomeViewModel() {
|
|
114
|
+
const repository = DataSourceResolver.repository;
|
|
115
|
+
const [items, setItems] = useState<Item[]>([]);
|
|
116
|
+
useEffect(() => {
|
|
117
|
+
setItems(repository.getItems());
|
|
118
|
+
const unsubscribe = repository.subscribe(() => setItems(repository.getItems()));
|
|
119
|
+
return unsubscribe;
|
|
120
|
+
}, []);
|
|
121
|
+
return { items };
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
**OfflineBanner integration** — add to main navigation screens:
|
|
126
|
+
```tsx
|
|
127
|
+
<View style={{ flex: 1 }}>
|
|
128
|
+
<OfflineBanner />
|
|
129
|
+
{/* ... rest of screen content */}
|
|
130
|
+
</View>
|
|
131
|
+
```
|
|
106
132
|
|
|
107
|
-
### Step 6 —
|
|
133
|
+
### Step 6 — Data Layer Verification
|
|
108
134
|
|
|
109
|
-
Verify
|
|
110
|
-
-
|
|
111
|
-
-
|
|
135
|
+
Verify:
|
|
136
|
+
- `DataRepository` interface matches MockDataProvider's public methods
|
|
137
|
+
- MockDataProvider implements DataRepository
|
|
138
|
+
- `DataSourceResolver` returns the correct implementation
|
|
139
|
+
- ViewModel hooks use `DataSourceResolver.repository`, not `MockDataProvider` directly
|
|
112
140
|
- Seed data matches the new domain
|
|
113
141
|
- CRUD methods reference correct entity types
|
|
114
142
|
|
|
143
|
+
### Step 6.5 — Apply Animation Modifiers (if animation modules are integrated)
|
|
144
|
+
|
|
145
|
+
If the manifest includes an **"Animation & Sensory Plan"** section:
|
|
146
|
+
|
|
147
|
+
1. Read the "Per-Screen Animation Modifiers" table
|
|
148
|
+
2. Apply the assigned modifiers using imported module hooks/components:
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
// MicroInteractions — press feedback
|
|
152
|
+
import { useScaleOnPress } from '../shared/MicroInteractions';
|
|
153
|
+
import { useHapticEngine } from '../shared/HapticEngine';
|
|
154
|
+
const { animatedStyle, handlers } = useScaleOnPress();
|
|
155
|
+
|
|
156
|
+
// ScrollEffects — parallax header
|
|
157
|
+
import { ParallaxHeader } from '../shared/ScrollEffects';
|
|
158
|
+
<ParallaxHeader height={280} imageSource={item.imageUrl} />
|
|
159
|
+
|
|
160
|
+
// CelebrationEffects — success overlay
|
|
161
|
+
import { CelebrationOverlay } from '../shared/CelebrationEffects';
|
|
162
|
+
<CelebrationOverlay isActive={showCelebration} style="confetti" />
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
3. Wrap with `useMotionPreferences` checks if MotionPreferences is integrated
|
|
166
|
+
4. Do NOT add animation modifiers to screens not listed in the manifest
|
|
167
|
+
|
|
168
|
+
### Step 6.7 — App Icon (if applicable)
|
|
169
|
+
|
|
170
|
+
React Native apps target both iOS and Android. App icon customization is handled per-platform:
|
|
171
|
+
- **iOS**: If `scripts/generate-app-icon.swift` exists, run it with SF Symbol + background color from manifest
|
|
172
|
+
- **Android**: If `templates/android/ICON_VECTORS.md` exists, update `ic_launcher_foreground.xml` and `ic_launcher_background` color
|
|
173
|
+
|
|
174
|
+
If scripts/resources are not available, skip — default icons will be used.
|
|
175
|
+
|
|
115
176
|
## Report Output
|
|
116
177
|
|
|
117
178
|
Write the report to `output/{app-name}/reports/05-customization.md`:
|
|
@@ -144,6 +205,17 @@ React Native (TypeScript)
|
|
|
144
205
|
- Empty states: {count}
|
|
145
206
|
- CTAs updated: {count}
|
|
146
207
|
|
|
208
|
+
## Offline Infrastructure
|
|
209
|
+
|
|
210
|
+
- OfflineBanner integrated: {yes/no}
|
|
211
|
+
- SyncManager methods updated: {list or "no changes needed"}
|
|
212
|
+
- Data persistence verified: {yes/no}
|
|
213
|
+
|
|
214
|
+
## Animation Modifiers Applied
|
|
215
|
+
|
|
216
|
+
- {modifier}: applied to {screen} — {description}
|
|
217
|
+
- MotionPreferences respected: {yes/no/N/A}
|
|
218
|
+
|
|
147
219
|
## Potential Issues
|
|
148
220
|
|
|
149
221
|
- {any concerns}
|
|
@@ -191,8 +263,10 @@ Templates include `testID` props on all interactive elements and a `TESTING_MANI
|
|
|
191
263
|
- ONLY edit files under `output/` — never touch `templates/`
|
|
192
264
|
- Ensure all imports resolve after your changes
|
|
193
265
|
- Preserve `MockDataProvider` CRUD methods and `reset()` function
|
|
266
|
+
- Do NOT modify NetworkMonitor, LocalPersistence, or OfflineBanner
|
|
267
|
+
- Ensure OfflineBanner is integrated into main navigation screens
|
|
194
268
|
- Use `testID` props on ALL interactive elements
|
|
195
|
-
- Follow `{screen}_{element}_{role}` convention for new test IDs
|
|
269
|
+
- Follow `{screen}_{element}_{role}` convention for new test IDs (see `templates/TEST_ID_CONVENTIONS.md`)
|
|
196
270
|
- Use `StyleSheet.create()` for styles, not inline objects
|
|
197
271
|
- Use functional components with TypeScript
|
|
198
272
|
- Keep React Navigation structure intact
|
|
@@ -123,18 +123,19 @@ private val LightColorScheme = lightColorScheme(
|
|
|
123
123
|
- Use Material 3 color roles (primary, secondary, tertiary, surface, etc.)
|
|
124
124
|
- Keep `dynamicColor` support if present
|
|
125
125
|
|
|
126
|
-
###
|
|
127
|
-
|
|
126
|
+
### Data Layer
|
|
127
|
+
Templates use a `DataRepository` interface with `DataSourceResolver`:
|
|
128
128
|
```kotlin
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
129
|
+
// Screens get the repository from DataSourceResolver
|
|
130
|
+
val repository = remember { DataSourceResolver.repository }
|
|
131
|
+
val items by repository.items.collectAsState()
|
|
132
|
+
repository.addItem(newItem)
|
|
133
133
|
```
|
|
134
|
-
-
|
|
135
|
-
-
|
|
136
|
-
-
|
|
137
|
-
-
|
|
134
|
+
- ALWAYS use `DataSourceResolver.repository`, never `MockDataProvider` directly
|
|
135
|
+
- Preserve MockDataProvider CRUD functions and `reset()` method
|
|
136
|
+
- Do NOT modify NetworkMonitor, LocalPersistence, or OfflineBanner
|
|
137
|
+
- Verify NetworkMonitor is registered in the Application class
|
|
138
|
+
- Ensure OfflineBanner is integrated into main navigation composables
|
|
138
139
|
|
|
139
140
|
## Output
|
|
140
141
|
|
|
@@ -155,5 +156,8 @@ POTENTIAL ISSUES:
|
|
|
155
156
|
- Ensure all `import` statements resolve after renaming
|
|
156
157
|
- Keep `@HiltAndroidApp`, `@HiltViewModel`, `@AndroidEntryPoint` intact
|
|
157
158
|
- Keep `build.gradle.kts` syntactically valid Kotlin DSL
|
|
158
|
-
- Preserve `MockDataProvider`
|
|
159
|
+
- Preserve `MockDataProvider` CRUD functions and `reset()` method
|
|
160
|
+
- Do NOT modify NetworkMonitor, LocalPersistence, or OfflineBanner
|
|
159
161
|
- Use Material 3 components (not legacy Material 2)
|
|
162
|
+
- Preserve ALL `Modifier.testTag()` calls — do NOT remove test IDs
|
|
163
|
+
- Follow `{screen}_{element}_{role}` convention for new test tags (see `templates/TEST_ID_CONVENTIONS.md`)
|