@leejungkiin/awkit 1.0.5 β 1.0.7
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/VERSION +1 -1
- package/package.json +2 -2
- package/skills/smali-to-kotlin/SKILL.md +85 -331
- package/skills/smali-to-kotlin/phase-0-discovery.md +129 -0
- package/skills/smali-to-kotlin/phase-1-architecture.md +157 -0
- package/skills/smali-to-kotlin/phase-2-blueprint-ui.md +347 -0
- package/skills/smali-to-kotlin/phase-3-logic-build.md +268 -0
- package/skills/smali-to-swift/SKILL.md +91 -532
- package/skills/smali-to-swift/phase-0-discovery.md +154 -0
- package/skills/smali-to-swift/phase-1-architecture.md +173 -0
- package/skills/smali-to-swift/phase-2-blueprint-ui.md +348 -0
- package/skills/smali-to-swift/phase-3-logic-build.md +312 -0
- package/workflows/mobile/reverse-android-build.md +119 -79
- package/workflows/mobile/reverse-android-design.md +10 -7
- package/workflows/mobile/reverse-android.md +52 -46
- package/workflows/mobile/reverse-ios-build.md +161 -50
- package/workflows/mobile/reverse-ios-design.md +10 -1
- package/workflows/mobile/reverse-ios.md +49 -37
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# ποΈ Phase 1: Architecture (District View) β Android
|
|
2
|
+
|
|
3
|
+
> **Zoom Level:** 1 β Architecture Design
|
|
4
|
+
> **Goal:** Design overall architecture with UI-First build order. NO code bodies.
|
|
5
|
+
> **Input:** Completed App Map from Phase 0.
|
|
6
|
+
> **Output:** Architecture Blueprint document.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## β OUTPUT RULE
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
β
ALLOWED: Architecture diagrams, layer maps, feature-to-file mapping, API endpoint tables
|
|
14
|
+
β
ALLOWED: File paths, package names, dependency lists
|
|
15
|
+
β οΈ ALLOWED: build.gradle.kts skeleton (plugins + dependencies only)
|
|
16
|
+
β BLOCKED: Function bodies, implementation details, actual Kotlin code
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## π Sub-steps
|
|
22
|
+
|
|
23
|
+
### 1.1: Layer Design
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
βββββββββββββββββββββββββββββββββββββββ
|
|
27
|
+
β Presentation β
|
|
28
|
+
β βββ screens/ ([N] screens) β
|
|
29
|
+
β βββ navigation/ (NavGraph) β
|
|
30
|
+
β βββ theme/ (Material 3) β
|
|
31
|
+
β βββ components/ (Shared composables)β
|
|
32
|
+
βββββββββββββββββββββββββββββββββββββββ€
|
|
33
|
+
β Domain β
|
|
34
|
+
β βββ model/ ([N] business models) β
|
|
35
|
+
β βββ repository/ ([N] interfaces) β
|
|
36
|
+
β βββ usecase/ ([N] use cases) β
|
|
37
|
+
βββββββββββββββββββββββββββββββββββββββ€
|
|
38
|
+
β Data β
|
|
39
|
+
β βββ remote/ ([N] API services) β
|
|
40
|
+
β βββ local/ (Room, DataStore) β
|
|
41
|
+
β βββ repository/ (implementations) β
|
|
42
|
+
βββββββββββββββββββββββββββββββββββββββ€
|
|
43
|
+
β DI / Utilities β
|
|
44
|
+
β βββ modules, extensions, crypto β
|
|
45
|
+
βββββββββββββββββββββββββββββββββββββββ
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 1.2: Feature β File Mapping
|
|
49
|
+
|
|
50
|
+
| Feature | Domain Model | Repository | UseCase | Screen | ViewModel |
|
|
51
|
+
|---------|-------------|-----------|---------|--------|-----------|
|
|
52
|
+
| Auth | User, Token | AuthRepo | LoginUC | Login | AuthVM |
|
|
53
|
+
| Home | Feed | HomeRepo | GetFeedUC | Home | HomeVM |
|
|
54
|
+
| ... | ... | ... | ... | ... | ... |
|
|
55
|
+
|
|
56
|
+
### 1.3: API Endpoint Inventory
|
|
57
|
+
|
|
58
|
+
| # | Method | Endpoint | Auth | Request | Response | Notes |
|
|
59
|
+
|---|--------|----------|------|---------|----------|-------|
|
|
60
|
+
| 1 | POST | /auth/login | No | email, pwd | JWT | - |
|
|
61
|
+
| 2 | GET | /users/me | Bearer | - | User | - |
|
|
62
|
+
|
|
63
|
+
**How to find in Smali:**
|
|
64
|
+
```
|
|
65
|
+
const-string β "https://" or "http://" (base URL)
|
|
66
|
+
.field β BASE_URL, API_URL
|
|
67
|
+
StringBuilder + append (endpoint construction)
|
|
68
|
+
Annotation patterns (@GET, @POST in retrofit)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### 1.4: Data Schema Inventory
|
|
72
|
+
|
|
73
|
+
| Model | Key Fields | Source | Storage |
|
|
74
|
+
|-------|-----------|--------|---------|
|
|
75
|
+
| User | id, name, email, avatar | API | Room + Memory |
|
|
76
|
+
| Settings | theme, language | Local | DataStore |
|
|
77
|
+
|
|
78
|
+
### 1.5: Project Structure
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
app/src/main/java/com/package/
|
|
82
|
+
βββ App.kt
|
|
83
|
+
βββ di/ (AppModule, NetworkModule, DatabaseModule)
|
|
84
|
+
βββ data/
|
|
85
|
+
β βββ remote/api/
|
|
86
|
+
β βββ remote/dto/
|
|
87
|
+
β βββ local/db/
|
|
88
|
+
β βββ local/datastore/
|
|
89
|
+
β βββ repository/
|
|
90
|
+
βββ domain/
|
|
91
|
+
β βββ model/
|
|
92
|
+
β βββ repository/
|
|
93
|
+
β βββ usecase/
|
|
94
|
+
βββ presentation/
|
|
95
|
+
β βββ navigation/
|
|
96
|
+
β βββ theme/
|
|
97
|
+
β βββ components/
|
|
98
|
+
β βββ screens/
|
|
99
|
+
β βββ splash/
|
|
100
|
+
β βββ auth/
|
|
101
|
+
β βββ home/
|
|
102
|
+
β βββ [feature]/
|
|
103
|
+
βββ util/
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### 1.6: Build Order (UI-First) β
|
|
107
|
+
|
|
108
|
+
> **IMPORTANT:** UI-First build order β design and approve UI before coding logic.
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
Foundation (one-time):
|
|
112
|
+
1. π’ Project setup + DI skeleton + Theme/Design System
|
|
113
|
+
2. π’ Navigation structure (routes, tab bar)
|
|
114
|
+
|
|
115
|
+
Per Feature (repeat for each):
|
|
116
|
+
3. π Blueprint: Contracts (models, repos, use cases, state)
|
|
117
|
+
4. π¨ UI Shell: Visual implementation + Resource extraction
|
|
118
|
+
5. π¦ GATE: User approve UI
|
|
119
|
+
6. π¨ Logic: Domain β Data β ViewModel β Wire UI
|
|
120
|
+
7. π§ͺ Test: Integration + Parity
|
|
121
|
+
|
|
122
|
+
Final (one-time):
|
|
123
|
+
8. π¦ SDK Integration + Native libs
|
|
124
|
+
9. β
Full Parity Check
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### 1.7: Tech Stack Decisions
|
|
128
|
+
|
|
129
|
+
| Decision | Choice | Rationale |
|
|
130
|
+
|----------|--------|-----------|
|
|
131
|
+
| Image Loading | Coil | Modern, Compose-native |
|
|
132
|
+
| JSON | Kotlin Serialization | Type-safe, no reflection |
|
|
133
|
+
| ... | ... | ... |
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## π Output: Architecture Blueprint
|
|
138
|
+
|
|
139
|
+
Use template from `templates/architecture.md`.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## β
Gate
|
|
144
|
+
|
|
145
|
+
```
|
|
146
|
+
"ποΈ Architecture Blueprint xong.
|
|
147
|
+
Build order: UI-First per feature.
|
|
148
|
+
Anh muα»n bαΊ―t ΔαΊ§u tα»« feature nΓ o?
|
|
149
|
+
Em suggest: [Feature X] vΓ¬ [reason β e.g., nhiα»u feature khΓ‘c phα»₯ thuα»c]."
|
|
150
|
+
|
|
151
|
+
β User picks feature β Phase 2 (Blueprint + UI Design)
|
|
152
|
+
β User wants changes β Adjust architecture
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
*Phase 1: Architecture β Design before you build, UI before logic*
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
# π Phase 2: Blueprint + UI Design (Per Feature) β Android
|
|
2
|
+
|
|
3
|
+
> **Zoom Level:** 2 β Feature Detail
|
|
4
|
+
> **Goal:** Design contracts AND code UI visual shell for ONE feature.
|
|
5
|
+
> **Input:** Architecture Blueprint (Phase 1) + user's chosen feature.
|
|
6
|
+
> **Output:** Approved contracts + Working UI shell with mock data.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## β OUTPUT RULE
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
PART A β Contracts (signatures only):
|
|
14
|
+
β
Interface signatures, data class definitions, sealed classes
|
|
15
|
+
β
Retrofit interface with @GET/@POST annotations (no body)
|
|
16
|
+
β
UiState sealed class, Event sealed class, Action sealed class
|
|
17
|
+
β Function body implementations (use TODO())
|
|
18
|
+
|
|
19
|
+
PART B β UI (visual code):
|
|
20
|
+
β
Full Jetpack Compose code for screen
|
|
21
|
+
β
Hardcoded mock data (using UiState from Part A)
|
|
22
|
+
β
Real resources (icons, colors, images extracted in 2.7)
|
|
23
|
+
β
@Preview for ALL states (normal, loading, error, empty)
|
|
24
|
+
β Real ViewModel connection β use parameter defaults
|
|
25
|
+
β Real API calls
|
|
26
|
+
β Business logic
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## π Sub-steps
|
|
32
|
+
|
|
33
|
+
### 2.1: Deep Smali Reading
|
|
34
|
+
|
|
35
|
+
Read Smali/Java files for the chosen feature.
|
|
36
|
+
|
|
37
|
+
**What to extract:**
|
|
38
|
+
- Class hierarchy (extends, implements)
|
|
39
|
+
- Field declarations β model properties
|
|
40
|
+
- Method signatures β API contracts
|
|
41
|
+
- String constants β URLs, keys, messages
|
|
42
|
+
- Control flow β business rules (document, don't code)
|
|
43
|
+
|
|
44
|
+
**Smali Quick Ref:**
|
|
45
|
+
```
|
|
46
|
+
.field β class fields (properties)
|
|
47
|
+
.method β method start
|
|
48
|
+
.end method β method end
|
|
49
|
+
invoke-virtual β instance method call
|
|
50
|
+
invoke-static β static method call
|
|
51
|
+
const-string β string literal
|
|
52
|
+
new-instance β object creation
|
|
53
|
+
if-eqz/if-nez β conditional branches
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
See `smali-reading-guide.md` for comprehensive guide.
|
|
57
|
+
|
|
58
|
+
### 2.2: Domain Model Contracts
|
|
59
|
+
|
|
60
|
+
```kotlin
|
|
61
|
+
// Domain model
|
|
62
|
+
data class User(
|
|
63
|
+
val id: String,
|
|
64
|
+
val fullName: String,
|
|
65
|
+
val email: String,
|
|
66
|
+
val avatarUrl: String?,
|
|
67
|
+
val isVerified: Boolean
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
// DTO (from API)
|
|
71
|
+
@Serializable
|
|
72
|
+
data class UserDto(
|
|
73
|
+
@SerialName("user_id") val userId: String,
|
|
74
|
+
@SerialName("full_name") val fullName: String,
|
|
75
|
+
@SerialName("email") val email: String,
|
|
76
|
+
@SerialName("avatar_url") val avatarUrl: String?,
|
|
77
|
+
@SerialName("is_verified") val isVerified: Boolean
|
|
78
|
+
)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### 2.3: Repository Contract
|
|
82
|
+
|
|
83
|
+
```kotlin
|
|
84
|
+
interface AuthRepository {
|
|
85
|
+
suspend fun login(email: String, password: String): Result<User>
|
|
86
|
+
suspend fun register(name: String, email: String, password: String): Result<User>
|
|
87
|
+
suspend fun logout()
|
|
88
|
+
fun isLoggedIn(): Flow<Boolean>
|
|
89
|
+
fun getCurrentUser(): Flow<User?>
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### 2.4: API Contract
|
|
94
|
+
|
|
95
|
+
```kotlin
|
|
96
|
+
interface AuthApi {
|
|
97
|
+
@POST("auth/login")
|
|
98
|
+
suspend fun login(@Body request: LoginRequest): LoginResponse
|
|
99
|
+
|
|
100
|
+
@POST("auth/register")
|
|
101
|
+
suspend fun register(@Body request: RegisterRequest): RegisterResponse
|
|
102
|
+
|
|
103
|
+
@GET("auth/me")
|
|
104
|
+
suspend fun getCurrentUser(@Header("Authorization") token: String): UserDto
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### 2.5: UseCase Signatures
|
|
109
|
+
|
|
110
|
+
```kotlin
|
|
111
|
+
class LoginUseCase(private val authRepo: AuthRepository) {
|
|
112
|
+
suspend operator fun invoke(email: String, password: String): Result<User>
|
|
113
|
+
// Implementation: TODO()
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### 2.6: UI State Design
|
|
118
|
+
|
|
119
|
+
```kotlin
|
|
120
|
+
// State
|
|
121
|
+
data class LoginUiState(
|
|
122
|
+
val email: String = "",
|
|
123
|
+
val password: String = "",
|
|
124
|
+
val isLoading: Boolean = false,
|
|
125
|
+
val error: String? = null,
|
|
126
|
+
val isPasswordVisible: Boolean = false
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
// Events (one-time actions)
|
|
130
|
+
sealed interface LoginEvent {
|
|
131
|
+
data class NavigateToHome(val user: User) : LoginEvent
|
|
132
|
+
data class ShowSnackbar(val message: String) : LoginEvent
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Actions (user interactions)
|
|
136
|
+
sealed interface LoginAction {
|
|
137
|
+
data class UpdateEmail(val email: String) : LoginAction
|
|
138
|
+
data class UpdatePassword(val password: String) : LoginAction
|
|
139
|
+
data object TogglePasswordVisibility : LoginAction
|
|
140
|
+
data object Submit : LoginAction
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### 2.7: Resource Extraction β (Before UI code!)
|
|
145
|
+
|
|
146
|
+
> **Why here?** UI code (2.8) needs real assets. If resources come later, UI will be full of placeholders β defeating the purpose of visual parity.
|
|
147
|
+
|
|
148
|
+
**Process:**
|
|
149
|
+
1. Analyze layout XML to list needed resources:
|
|
150
|
+
```bash
|
|
151
|
+
grep -o '@drawable/[a-z_]*' [apktool]/res/layout/activity_login.xml | sort -u
|
|
152
|
+
grep -o '@color/[a-z_]*' [apktool]/res/layout/activity_login.xml | sort -u
|
|
153
|
+
grep 'const-string' [apktool]/smali/.../LoginActivity.smali | grep -i string
|
|
154
|
+
```
|
|
155
|
+
2. Copy ONLY needed resources to new project
|
|
156
|
+
3. Verify all resources compile
|
|
157
|
+
|
|
158
|
+
**Output:**
|
|
159
|
+
```markdown
|
|
160
|
+
### π¦ Resources for [Screen]
|
|
161
|
+
- Drawables: [list]
|
|
162
|
+
- Colors: [list]
|
|
163
|
+
- Strings: [list]
|
|
164
|
+
- Dimens: [list]
|
|
165
|
+
β
All accessible
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### 2.8: UI Implementation β Visual Shell β
|
|
169
|
+
|
|
170
|
+
> **Goal:** Code the screen with HARDCODED mock data. Pixel-perfect visual parity with original app.
|
|
171
|
+
|
|
172
|
+
**Rules:**
|
|
173
|
+
```
|
|
174
|
+
β
MUST DO:
|
|
175
|
+
- Use UiState from 2.6 (hardcode sample values as defaults)
|
|
176
|
+
- Use REAL resources extracted in 2.7
|
|
177
|
+
- Display ALL visual elements from original app
|
|
178
|
+
- Match: spacing, font size, colors, icon placement
|
|
179
|
+
- Code ALL states: normal, loading, error, empty
|
|
180
|
+
- Create @Preview for each state
|
|
181
|
+
|
|
182
|
+
β MUST NOT:
|
|
183
|
+
- Connect real ViewModel (use parameter defaults)
|
|
184
|
+
- Call real APIs
|
|
185
|
+
- Code business logic
|
|
186
|
+
- Use DI/injection
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
**Pattern β Stateless Composable:**
|
|
190
|
+
```kotlin
|
|
191
|
+
@Composable
|
|
192
|
+
fun LoginScreenContent(
|
|
193
|
+
uiState: LoginUiState = LoginUiState(), // Hardcoded default
|
|
194
|
+
onAction: (LoginAction) -> Unit = {} // No-op default
|
|
195
|
+
) {
|
|
196
|
+
Scaffold { padding ->
|
|
197
|
+
Column(
|
|
198
|
+
modifier = Modifier
|
|
199
|
+
.fillMaxSize()
|
|
200
|
+
.padding(padding)
|
|
201
|
+
.padding(24.dp),
|
|
202
|
+
horizontalAlignment = Alignment.CenterHorizontally
|
|
203
|
+
) {
|
|
204
|
+
// Logo β real resource from 2.7
|
|
205
|
+
Image(
|
|
206
|
+
painter = painterResource(R.drawable.app_logo),
|
|
207
|
+
contentDescription = null,
|
|
208
|
+
modifier = Modifier.size(120.dp)
|
|
209
|
+
)
|
|
210
|
+
Spacer(Modifier.height(48.dp))
|
|
211
|
+
|
|
212
|
+
// Email
|
|
213
|
+
OutlinedTextField(
|
|
214
|
+
value = uiState.email,
|
|
215
|
+
onValueChange = { onAction(LoginAction.UpdateEmail(it)) },
|
|
216
|
+
label = { Text("Email") },
|
|
217
|
+
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Email),
|
|
218
|
+
modifier = Modifier.fillMaxWidth()
|
|
219
|
+
)
|
|
220
|
+
Spacer(Modifier.height(16.dp))
|
|
221
|
+
|
|
222
|
+
// Password with toggle
|
|
223
|
+
OutlinedTextField(
|
|
224
|
+
value = uiState.password,
|
|
225
|
+
onValueChange = { onAction(LoginAction.UpdatePassword(it)) },
|
|
226
|
+
label = { Text("Password") },
|
|
227
|
+
visualTransformation = if (uiState.isPasswordVisible)
|
|
228
|
+
VisualTransformation.None else PasswordVisualTransformation(),
|
|
229
|
+
trailingIcon = {
|
|
230
|
+
IconButton(onClick = { onAction(LoginAction.TogglePasswordVisibility) }) {
|
|
231
|
+
Icon(
|
|
232
|
+
imageVector = if (uiState.isPasswordVisible)
|
|
233
|
+
Icons.Default.VisibilityOff else Icons.Default.Visibility,
|
|
234
|
+
contentDescription = "Toggle password"
|
|
235
|
+
)
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
modifier = Modifier.fillMaxWidth()
|
|
239
|
+
)
|
|
240
|
+
Spacer(Modifier.height(32.dp))
|
|
241
|
+
|
|
242
|
+
// Login Button
|
|
243
|
+
Button(
|
|
244
|
+
onClick = { onAction(LoginAction.Submit) },
|
|
245
|
+
enabled = !uiState.isLoading,
|
|
246
|
+
modifier = Modifier.fillMaxWidth().height(50.dp)
|
|
247
|
+
) {
|
|
248
|
+
if (uiState.isLoading) {
|
|
249
|
+
CircularProgressIndicator(
|
|
250
|
+
modifier = Modifier.size(24.dp),
|
|
251
|
+
color = MaterialTheme.colorScheme.onPrimary
|
|
252
|
+
)
|
|
253
|
+
} else {
|
|
254
|
+
Text("Login", style = MaterialTheme.typography.labelLarge)
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// ===== PREVIEWS =====
|
|
262
|
+
@Preview(showBackground = true)
|
|
263
|
+
@Composable
|
|
264
|
+
private fun NormalPreview() {
|
|
265
|
+
AppTheme { LoginScreenContent(LoginUiState(email = "user@example.com")) }
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
@Preview(showBackground = true)
|
|
269
|
+
@Composable
|
|
270
|
+
private fun LoadingPreview() {
|
|
271
|
+
AppTheme { LoginScreenContent(LoginUiState(isLoading = true)) }
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
@Preview(showBackground = true)
|
|
275
|
+
@Composable
|
|
276
|
+
private fun ErrorPreview() {
|
|
277
|
+
AppTheme { LoginScreenContent(LoginUiState(error = "Invalid credentials")) }
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### 2.9: Visual Parity Check β
|
|
282
|
+
|
|
283
|
+
Compare UI shell with original app screenshot:
|
|
284
|
+
|
|
285
|
+
```markdown
|
|
286
|
+
### πΈ Visual Parity: [Screen Name]
|
|
287
|
+
|
|
288
|
+
Layout:
|
|
289
|
+
- [ ] Structure matches original (components, sections)
|
|
290
|
+
- [ ] Spacing between elements correct
|
|
291
|
+
- [ ] Alignment matches
|
|
292
|
+
|
|
293
|
+
Styling:
|
|
294
|
+
- [ ] Colors match (background, text, buttons, header)
|
|
295
|
+
- [ ] Typography matches (font size, weight)
|
|
296
|
+
- [ ] Icons/images correct and positioned
|
|
297
|
+
- [ ] Borders, shadows, rounded corners
|
|
298
|
+
|
|
299
|
+
States:
|
|
300
|
+
- [ ] Normal state displays correctly
|
|
301
|
+
- [ ] Loading state β progress in right position
|
|
302
|
+
- [ ] Error state β error message shows properly
|
|
303
|
+
- [ ] Empty state β guidance shown
|
|
304
|
+
|
|
305
|
+
Platform:
|
|
306
|
+
- [ ] Material 3 conventions followed
|
|
307
|
+
- [ ] Edge-to-edge / system bars handled
|
|
308
|
+
- [ ] Looks good on different screen sizes
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
## π Output: Feature Blueprint + UI
|
|
314
|
+
|
|
315
|
+
Use template from `templates/blueprint.md`.
|
|
316
|
+
|
|
317
|
+
---
|
|
318
|
+
|
|
319
|
+
## β
Gate (MANDATORY)
|
|
320
|
+
|
|
321
|
+
```
|
|
322
|
+
"π Blueprint + UI cho [Feature] xong:
|
|
323
|
+
|
|
324
|
+
π Contracts:
|
|
325
|
+
- [N] domain models
|
|
326
|
+
- [N] repository interfaces
|
|
327
|
+
- [N] use case signatures
|
|
328
|
+
- [N] API endpoints
|
|
329
|
+
|
|
330
|
+
π¨ UI:
|
|
331
|
+
- [Screen] implemented with mock data
|
|
332
|
+
- Resources: [N] drawables, [N] strings, [N] colors
|
|
333
|
+
- Previews: [N] states (normal, loading, error, empty)
|
|
334
|
+
|
|
335
|
+
πΈ Visual Parity: [OK / cαΊ§n chα»nh X, Y, Z]
|
|
336
|
+
|
|
337
|
+
Anh xem UI + contracts α»n khΓ΄ng?
|
|
338
|
+
β β
Approve β Phase 3 (Logic Build)
|
|
339
|
+
β π¨ Adjust UI β fix then re-check
|
|
340
|
+
β π Adjust contracts β revise blueprint"
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
> β οΈ **DO NOT proceed to Phase 3 until user approves BOTH UI and contracts.**
|
|
344
|
+
|
|
345
|
+
---
|
|
346
|
+
|
|
347
|
+
*Phase 2: Blueprint + UI Design β See it before you code it*
|