ima2-gen 2.0.15 → 2.0.16
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/README.md +30 -0
- package/bin/commands/skill.js +336 -17
- package/bin/ima2.js +22 -2
- package/bin/lib/doctor-checks.js +6 -4
- package/config.js +1 -0
- package/docs/CLI.md +25 -3
- package/docs/PROMPT_STUDIO.ko.md +29 -0
- package/docs/PROMPT_STUDIO.md +31 -0
- package/docs/migration/runtime-test-inventory.md +7 -1
- package/lib/agentImageVideoGen.js +8 -0
- package/lib/agentQueueStore.js +36 -10
- package/lib/agentQueueWorker.js +79 -7
- package/lib/agentRuntime.js +2 -0
- package/lib/assetLifecycle.js +40 -8
- package/lib/canvasVersionStore.js +86 -0
- package/lib/cardNewsGenerator.js +37 -3
- package/lib/cardNewsJobStore.js +10 -0
- package/lib/cardNewsManifestStore.js +6 -12
- package/lib/cardNewsPath.js +25 -0
- package/lib/db.js +2 -0
- package/lib/generatePipeline.js +474 -0
- package/lib/generationInputValidation.js +32 -0
- package/lib/grokImageCore.js +18 -1
- package/lib/historyIndex.js +11 -4
- package/lib/historyList.js +3 -0
- package/lib/imageMetadata.js +3 -0
- package/lib/inflight.js +30 -3
- package/lib/multimodePipeline.js +462 -0
- package/lib/nodeGeneration.js +440 -0
- package/lib/nodeValidation.js +23 -0
- package/lib/oauthProxy/generators.js +3 -246
- package/lib/oauthProxy/multimodeGenerators.js +251 -0
- package/lib/routeHelpers.js +9 -2
- package/package.json +2 -2
- package/routes/agent.js +4 -4
- package/routes/canvasVersions.js +21 -1
- package/routes/cardNews.js +2 -1
- package/routes/edit.js +25 -9
- package/routes/events.js +5 -1
- package/routes/generate.js +2 -481
- package/routes/keys.js +31 -39
- package/routes/multimode.js +2 -502
- package/routes/nodes.js +3 -448
- package/routes/video.js +12 -3
- package/routes/videoExtended.js +19 -4
- package/server.js +53 -1
- package/skills/ima2/SKILL.md +204 -0
- package/skills/ima2-front/SKILL.md +611 -0
- package/skills/ima2-front/references/a11y-patterns.md +134 -0
- package/skills/ima2-front/references/aesthetics.md +327 -0
- package/skills/ima2-front/references/anti-slop.md +241 -0
- package/skills/ima2-front/references/asset-requirements.md +431 -0
- package/skills/ima2-front/references/brand-asset-sourcing.md +122 -0
- package/skills/ima2-front/references/color-system.md +132 -0
- package/skills/ima2-front/references/consistency-locks.md +20 -0
- package/skills/ima2-front/references/crud-ui.md +53 -0
- package/skills/ima2-front/references/dropdown-layer.md +110 -0
- package/skills/ima2-front/references/i18n-global.md +104 -0
- package/skills/ima2-front/references/iterative-design.md +90 -0
- package/skills/ima2-front/references/korea-2026.md +174 -0
- package/skills/ima2-front/references/layout-discipline.md +161 -0
- package/skills/ima2-front/references/liquid-glass.md +181 -0
- package/skills/ima2-front/references/logo-sections.md +194 -0
- package/skills/ima2-front/references/mobile-ux.md +144 -0
- package/skills/ima2-front/references/performance-budget.md +106 -0
- package/skills/ima2-front/references/preflight-full.md +58 -0
- package/skills/ima2-front/references/product-density.md +53 -0
- package/skills/ima2-front/references/prototype-variants.md +54 -0
- package/skills/ima2-front/references/reference-capture.md +120 -0
- package/skills/ima2-front/references/responsive-viewport.md +139 -0
- package/skills/ima2-front/references/seo-baseline.md +123 -0
- package/skills/ima2-front/references/soft-3d-asset-gates.md +70 -0
- package/skills/ima2-front/references/stacks/astro.md +204 -0
- package/skills/ima2-front/references/stacks/mobile-native.md +281 -0
- package/skills/ima2-front/references/stacks/nextjs.md +219 -0
- package/skills/ima2-front/references/stacks/react.md +264 -0
- package/skills/ima2-front/references/stacks/svelte.md +235 -0
- package/skills/ima2-front/references/stacks/vanilla.md +226 -0
- package/skills/ima2-front/references/theme-switching.md +82 -0
- package/skills/ima2-front/references/top-bar.md +126 -0
- package/skills/ima2-front/references/typography-wrapping.md +270 -0
- package/skills/ima2-front/references/ux-writing-ko.md +62 -0
- package/skills/ima2-front/references/visual-verification.md +102 -0
- package/skills/ima2-uiux/SKILL.md +477 -0
- package/skills/ima2-uiux/references/anti-slop-judgment.md +39 -0
- package/skills/ima2-uiux/references/color-system.md +80 -0
- package/skills/ima2-uiux/references/design-isms.md +194 -0
- package/skills/ima2-uiux/references/design-read-example.md +33 -0
- package/skills/ima2-uiux/references/design-system-bootstrap.md +133 -0
- package/skills/ima2-uiux/references/favicon-logo.md +355 -0
- package/skills/ima2-uiux/references/form-patterns.md +118 -0
- package/skills/ima2-uiux/references/intent-discovery-ladder.md +86 -0
- package/skills/ima2-uiux/references/korean-design-vocabulary.md +52 -0
- package/skills/ima2-uiux/references/layout-macrostructures.md +54 -0
- package/skills/ima2-uiux/references/logo-trust-sections.md +96 -0
- package/skills/ima2-uiux/references/mobile-native-ux.md +207 -0
- package/skills/ima2-uiux/references/product-personalities.md +118 -0
- package/skills/ima2-uiux/references/responsive-nav.md +66 -0
- package/skills/ima2-uiux/references/typography-line-breaks.md +193 -0
- package/skills/ima2-uiux/references/ux-preflight.md +65 -0
- package/skills/ima2-uiux/references/ux-states.md +197 -0
- package/skills/ima2-uiux/references/visual-hierarchy.md +115 -0
- package/ui/dist/.vite/manifest.json +13 -13
- package/ui/dist/assets/AgentWorkspace-CtYt4SF5.js +3 -0
- package/ui/dist/assets/{CardNewsWorkspace-Cdxn1ohU.js → CardNewsWorkspace-DN5cVqG5.js} +1 -1
- package/ui/dist/assets/GenerationRequestLogPanel-DnmUQdKR.js +1 -0
- package/ui/dist/assets/{NodeCanvas-C67sLboA.js → NodeCanvas-CxpfIBI0.js} +1 -1
- package/ui/dist/assets/PromptBuilderPanel-C2R3ObtR.js +2 -0
- package/ui/dist/assets/{PromptImportDialog-DeQXsEEv.js → PromptImportDialog-BIxz_yEx.js} +2 -2
- package/ui/dist/assets/PromptImportDiscoverySection-BQTlPMME.js +1 -0
- package/ui/dist/assets/PromptImportFolderSection-Ds_Th9O3.js +1 -0
- package/ui/dist/assets/{PromptLibraryPanel-Db3xEPM_.js → PromptLibraryPanel-Rs4l45pV.js} +2 -2
- package/ui/dist/assets/{SettingsWorkspace-BDk_1W-V.js → SettingsWorkspace-p1AZ6uT9.js} +1 -1
- package/ui/dist/assets/index-0-_vgFGs.js +4 -0
- package/ui/dist/assets/index-Dm3pFxV4.js +23 -0
- package/ui/dist/assets/index-J8yDF3Ch.css +1 -0
- package/ui/dist/index.html +2 -2
- package/ui/dist/assets/AgentWorkspace-DAg_7zpY.js +0 -3
- package/ui/dist/assets/GenerationRequestLogPanel-C_qvEUi2.js +0 -1
- package/ui/dist/assets/PromptBuilderPanel-DjvVdAie.js +0 -2
- package/ui/dist/assets/PromptImportDiscoverySection-CrwyUKYI.js +0 -1
- package/ui/dist/assets/PromptImportFolderSection-CToGMqYE.js +0 -1
- package/ui/dist/assets/index-2oG6in1i.css +0 -1
- package/ui/dist/assets/index-CoxMOXAU.js +0 -23
- package/ui/dist/assets/index-p8egnGHk.js +0 -4
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
# Mobile Native Development
|
|
2
|
+
|
|
3
|
+
**Last reviewed**: 2026-07-02
|
|
4
|
+
**Applies to**: Expo SDK 57 / React Native 0.86 / React 19.2.3, Flutter 3.44+, Kotlin Multiplatform 2.2+, Swift 6, Jetpack Compose M3 1.4+
|
|
5
|
+
**When to read**: Native mobile app development, cross-platform framework selection, change-surface: native mobile
|
|
6
|
+
**Canonical owner**: `ima2-front` — framework selection and component patterns
|
|
7
|
+
**Non-goals**: Mobile web responsive (→ `responsive-viewport.md`, `mobile-ux.md`), push/offline/auth API (→ `dev-backend/references/core/mobile-api.md`), native UX conventions (→ `dev-uiux-design/references/mobile-native-ux.md`)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## §1 Domain Routing
|
|
12
|
+
|
|
13
|
+
| Need | Go To |
|
|
14
|
+
|------|-------|
|
|
15
|
+
| Framework selection + component patterns | This file |
|
|
16
|
+
| iOS HIG vs Material 3, gestures, deep linking | `dev-uiux-design/references/mobile-native-ux.md` |
|
|
17
|
+
| Push notifications, offline sync, mobile BFF | `dev-backend/references/core/mobile-api.md` |
|
|
18
|
+
| Mobile web responsive layout | `dev-frontend/references/core/responsive-viewport.md` |
|
|
19
|
+
| Mobile web UX (thumb zone, sticky CTA) | `dev-frontend/references/core/mobile-ux.md` |
|
|
20
|
+
| App store screenshots, icon specs | `dev-uiux-design/references/mobile-native-ux.md` §5 |
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## §2 Framework Selection 2026
|
|
25
|
+
|
|
26
|
+
| Criterion | React Native + Expo | Flutter | Kotlin Multiplatform | Swift (iOS) / Kotlin (Android) |
|
|
27
|
+
|-----------|-------------------|---------|---------------------|-------------------------------|
|
|
28
|
+
| **Language** | TypeScript | Dart | Kotlin (shared) + Swift/Kotlin (UI) | Swift / Kotlin |
|
|
29
|
+
| **Rendering** | Native views via Fabric | Skia/Impeller (own canvas) | Native views per platform | Native views |
|
|
30
|
+
| **Code sharing** | ~90% (JS logic + RN components) | ~95% (single widget tree) | ~70% (logic only, UI per platform) | 0% (per platform) |
|
|
31
|
+
| **AI agent friendliness** | High — TS ecosystem, npm, large training corpus | Medium — Dart has smaller corpus, widget DSL learning curve | Medium — Kotlin corpus good, KMP-specific patterns sparse | Medium — per-platform, no sharing |
|
|
32
|
+
| **Hot reload** | Yes (Metro + Hermes) | Yes (Dart VM) | Partial (compose preview) | Xcode previews (limited) |
|
|
33
|
+
| **2026 state** | New Architecture default, Hermes, Expo SDK 57 / RN 0.86 | 3.44 SPM default, Impeller stable | KotlinConf'26 restructure, Compose Multiplatform 1.8 | Swift 6 strict concurrency, SwiftUI 6 |
|
|
34
|
+
| **Best for** | JS/TS teams, rapid iteration, Expo managed workflow | Pixel-perfect custom UI, animation-heavy apps | Existing Kotlin backend teams sharing logic | Single-platform apps needing full native API access |
|
|
35
|
+
|
|
36
|
+
**Default recommendation**: React Native + Expo for most cross-platform projects. Switch to Flutter for heavy custom UI/animation, KMP for Kotlin-first backend teams sharing domain logic, native-only when platform API depth demands it.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## §3 React Native + Expo (Default Path)
|
|
41
|
+
|
|
42
|
+
### Expo SDK 57 baseline (2026-07)
|
|
43
|
+
|
|
44
|
+
| Feature | Status | Notes |
|
|
45
|
+
|---------|--------|-------|
|
|
46
|
+
| New Architecture | Default on | Fabric renderer + TurboModules; opt-out deprecated |
|
|
47
|
+
| Hermes | Default engine | Use Expo defaults unless a native constraint proves otherwise |
|
|
48
|
+
| `expo/fetch` | Stable | WinterCG-compatible fetch, replaces `whatwg-fetch` polyfill |
|
|
49
|
+
| Expo Router | Stable | File-based routing, typed routes, API routes |
|
|
50
|
+
| EAS Build | Production | Cloud builds for iOS/Android, no local Xcode/Gradle needed |
|
|
51
|
+
| EAS Submit | Production | Automated App Store Connect + Google Play upload |
|
|
52
|
+
| Expo Modules API | Stable | Swift/Kotlin native module authoring without ejecting |
|
|
53
|
+
|
|
54
|
+
Version grounding: Expo's SDK 57 release notes list React Native 0.86 and React
|
|
55
|
+
19.2.3 as the default pair. Verify live Expo docs before shipping a new mobile
|
|
56
|
+
baseline because Expo/RN pairings move together.
|
|
57
|
+
|
|
58
|
+
### TurboModule spec pattern
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
// specs/NativeDeviceInfo.ts
|
|
62
|
+
import type { TurboModule } from "react-native";
|
|
63
|
+
import { TurboModuleRegistry } from "react-native";
|
|
64
|
+
|
|
65
|
+
export interface Spec extends TurboModule {
|
|
66
|
+
getDeviceId(): string;
|
|
67
|
+
getBatteryLevel(): Promise<number>;
|
|
68
|
+
getLocale(): string;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export default TurboModuleRegistry.getEnforcing<Spec>("DeviceInfo");
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Project structure (Expo Router)
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
app/
|
|
78
|
+
(tabs)/
|
|
79
|
+
index.tsx # Home tab
|
|
80
|
+
settings.tsx # Settings tab
|
|
81
|
+
_layout.tsx # Tab navigator layout
|
|
82
|
+
(auth)/
|
|
83
|
+
login.tsx
|
|
84
|
+
register.tsx
|
|
85
|
+
_layout.tsx # Auth stack layout
|
|
86
|
+
_layout.tsx # Root layout (providers, fonts)
|
|
87
|
+
modules/ # Expo Modules (native Swift/Kotlin)
|
|
88
|
+
device-info/
|
|
89
|
+
src/DeviceInfoModule.swift
|
|
90
|
+
src/DeviceInfoModule.kt
|
|
91
|
+
expo-module.config.json
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### EAS Build + Submit workflow
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
// eas.json
|
|
98
|
+
{
|
|
99
|
+
"cli": { "version": ">= 14.0.0" },
|
|
100
|
+
"build": {
|
|
101
|
+
"development": {
|
|
102
|
+
"developmentClient": true,
|
|
103
|
+
"distribution": "internal",
|
|
104
|
+
"ios": { "simulator": true }
|
|
105
|
+
},
|
|
106
|
+
"preview": {
|
|
107
|
+
"distribution": "internal",
|
|
108
|
+
"channel": "preview"
|
|
109
|
+
},
|
|
110
|
+
"production": {
|
|
111
|
+
"channel": "production",
|
|
112
|
+
"autoIncrement": true
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
"submit": {
|
|
116
|
+
"production": {
|
|
117
|
+
"ios": { "appleId": "team@example.com", "ascAppId": "1234567890" },
|
|
118
|
+
"android": { "serviceAccountKeyPath": "./pc-api-key.json", "track": "internal" }
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
# Build + submit pipeline
|
|
126
|
+
eas build --platform all --profile production
|
|
127
|
+
eas submit --platform ios --profile production
|
|
128
|
+
eas submit --platform android --profile production
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## §4 Flutter (Custom UI Path)
|
|
134
|
+
|
|
135
|
+
### Flutter 3.44 baseline (2026-06)
|
|
136
|
+
|
|
137
|
+
| Feature | Status | Notes |
|
|
138
|
+
|---------|--------|-------|
|
|
139
|
+
| Impeller | Default (iOS + Android) | GPU-accelerated, no shader jank |
|
|
140
|
+
| SPM integration | Default for iOS | Replaces CocoaPods; `flutter pub add` handles native deps |
|
|
141
|
+
| Material 3 | Default theme | `ThemeData(useMaterial3: true)` is now the only path |
|
|
142
|
+
| Riverpod 3.x | Community standard | Compile-safe DI, code generation with `riverpod_generator` |
|
|
143
|
+
| Dart 3.7 | Stable | Sealed classes, pattern matching, class modifiers |
|
|
144
|
+
|
|
145
|
+
### State management decision
|
|
146
|
+
|
|
147
|
+
| Size | Approach | Package |
|
|
148
|
+
|------|----------|---------|
|
|
149
|
+
| Prototype / small | `ValueNotifier` + `ListenableBuilder` | Built-in |
|
|
150
|
+
| Medium app | Riverpod 3.x | `flutter_riverpod` + `riverpod_generator` |
|
|
151
|
+
| Large / team | Riverpod 3.x + `freezed` for immutable models | `riverpod` + `freezed` |
|
|
152
|
+
|
|
153
|
+
### Riverpod provider pattern
|
|
154
|
+
|
|
155
|
+
```dart
|
|
156
|
+
// providers/user_provider.dart
|
|
157
|
+
@riverpod
|
|
158
|
+
class UserNotifier extends _$UserNotifier {
|
|
159
|
+
@override
|
|
160
|
+
Future<User> build() => ref.read(userRepositoryProvider).getCurrentUser();
|
|
161
|
+
|
|
162
|
+
Future<void> updateName(String name) async {
|
|
163
|
+
state = const AsyncLoading();
|
|
164
|
+
state = await AsyncValue.guard(
|
|
165
|
+
() => ref.read(userRepositoryProvider).updateName(name),
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## §5 Kotlin Multiplatform
|
|
174
|
+
|
|
175
|
+
### KotlinConf'26 structure
|
|
176
|
+
|
|
177
|
+
| Layer | Shared (KMP) | Platform-specific |
|
|
178
|
+
|-------|-------------|-------------------|
|
|
179
|
+
| Domain models | `shared/src/commonMain/` | — |
|
|
180
|
+
| Use cases / business logic | `shared/src/commonMain/` | — |
|
|
181
|
+
| Networking (Ktor 3.x) | `shared/src/commonMain/` | Engine: `iosMain/` (Darwin), `androidMain/` (OkHttp) |
|
|
182
|
+
| UI | — | `androidApp/` (Compose), `iosApp/` (SwiftUI) |
|
|
183
|
+
| Compose Multiplatform 1.8 | Optional: shared UI for Android + Desktop | iOS support in beta |
|
|
184
|
+
|
|
185
|
+
### Shared module pattern
|
|
186
|
+
|
|
187
|
+
```kotlin
|
|
188
|
+
// shared/src/commonMain/kotlin/com/example/UserRepository.kt
|
|
189
|
+
class UserRepository(private val api: UserApi, private val db: UserDb) {
|
|
190
|
+
suspend fun getUser(id: String): User {
|
|
191
|
+
return db.getUser(id) ?: api.fetchUser(id).also { db.save(it) }
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// shared/src/commonMain/kotlin/com/example/UserApi.kt
|
|
196
|
+
class UserApi(private val client: HttpClient) {
|
|
197
|
+
suspend fun fetchUser(id: String): User =
|
|
198
|
+
client.get("users/$id").body()
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## §6 Swift / Kotlin Native
|
|
205
|
+
|
|
206
|
+
### Swift 6 (iOS native)
|
|
207
|
+
|
|
208
|
+
| Feature | Impact |
|
|
209
|
+
|---------|--------|
|
|
210
|
+
| Strict concurrency | `Sendable` enforcement by default; data races are compile errors |
|
|
211
|
+
| `@Observable` macro | Replaces `ObservableObject`; simpler SwiftUI state |
|
|
212
|
+
| Structured concurrency | `async let`, `TaskGroup`, `AsyncStream` as primary patterns |
|
|
213
|
+
| SwiftUI 6 | `@Bindable`, improved navigation (`NavigationStack`), `containerRelativeFrame` |
|
|
214
|
+
|
|
215
|
+
### Jetpack Compose M3 1.4+ (Android native)
|
|
216
|
+
|
|
217
|
+
| Feature | Impact |
|
|
218
|
+
|---------|--------|
|
|
219
|
+
| Material 3 Expressive | New components: `FloatingToolbar`, `LoadingIndicator`, shape morphing |
|
|
220
|
+
| Adaptive layouts | `ListDetailPaneScaffold` for tablet/foldable |
|
|
221
|
+
| Compose compiler 2.1 | K2 compiler, faster builds, strong skipping default |
|
|
222
|
+
| Lifecycle 2.9 | `collectAsStateWithLifecycle` for flow-safe collection |
|
|
223
|
+
|
|
224
|
+
### SwiftUI view pattern
|
|
225
|
+
|
|
226
|
+
```swift
|
|
227
|
+
@Observable
|
|
228
|
+
final class UserViewModel {
|
|
229
|
+
var user: User?
|
|
230
|
+
var isLoading = false
|
|
231
|
+
|
|
232
|
+
func load() async {
|
|
233
|
+
isLoading = true
|
|
234
|
+
defer { isLoading = false }
|
|
235
|
+
user = try? await UserService.shared.fetchUser()
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
struct UserView: View {
|
|
240
|
+
@State private var viewModel = UserViewModel()
|
|
241
|
+
|
|
242
|
+
var body: some View {
|
|
243
|
+
Group {
|
|
244
|
+
if viewModel.isLoading {
|
|
245
|
+
ProgressView()
|
|
246
|
+
} else if let user = viewModel.user {
|
|
247
|
+
Text(user.name)
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
.task { await viewModel.load() }
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## §7 Anti-Patterns & Pre-flight
|
|
258
|
+
|
|
259
|
+
### Anti-patterns
|
|
260
|
+
|
|
261
|
+
| Banned | Symptom | Fix |
|
|
262
|
+
|--------|---------|-----|
|
|
263
|
+
| `react-native link` in Expo managed | Build fails, native deps break | Use Expo Modules API or config plugins |
|
|
264
|
+
| JSC engine in production (RN 0.86+) | Slower cold start, no Intl support | Hermes is default; never opt out |
|
|
265
|
+
| New Architecture opt-out (`newArchEnabled: false`) | Missing Fabric perf, TurboModule access blocked | Remove opt-out; New Arch is default since 0.76 |
|
|
266
|
+
| CocoaPods in Flutter 3.44+ | Dep resolution conflicts with SPM | Migrate to SPM (`flutter pub deps --style=spm`) |
|
|
267
|
+
| `setState` everywhere in Flutter | Widget rebuilds cascade, jank on complex screens | Riverpod or `ValueNotifier` for state outside widgets |
|
|
268
|
+
| `ObservableObject` in Swift 6 | Verbose, requires `@Published` on every field | Migrate to `@Observable` macro |
|
|
269
|
+
| Blocking main thread for network (Kotlin) | ANR on Android, UI freeze | `suspend fun` + `Dispatchers.IO` for all I/O |
|
|
270
|
+
|
|
271
|
+
### Pre-flight checklist
|
|
272
|
+
|
|
273
|
+
- [ ] Framework selected using §2 decision table with documented rationale
|
|
274
|
+
- [ ] RN: Hermes V1 enabled, New Architecture active (`newArchEnabled` absent or `true`)
|
|
275
|
+
- [ ] RN: Expo SDK 57 / React Native 0.86 / React 19.2.3 with `expo/fetch`, no `whatwg-fetch` polyfill
|
|
276
|
+
- [ ] Flutter: Impeller enabled (default), SPM for iOS deps (no CocoaPods)
|
|
277
|
+
- [ ] KMP: Shared module compiles for all target platforms (`./gradlew :shared:allTests`)
|
|
278
|
+
- [ ] Native: Swift 6 strict concurrency mode enabled; Compose compiler 2.1+
|
|
279
|
+
- [ ] Deep linking configured (→ `mobile-native-ux.md` §3)
|
|
280
|
+
- [ ] Push notification token management (→ `mobile-api.md` §2)
|
|
281
|
+
- [ ] App store assets prepared (→ `mobile-native-ux.md` §5)
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# Next.js Stack — Development Rules
|
|
2
|
+
|
|
3
|
+
Extends `stacks/react.md` — read React rules first. These are Next.js-specific additions.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Visual Assets
|
|
8
|
+
|
|
9
|
+
Use Next image/media features when the surface needs concrete visuals:
|
|
10
|
+
|
|
11
|
+
- product/place/person/object pages need a real or generated subject image in the first viewport
|
|
12
|
+
- screenshots and workflow images should use `next/image` when practical
|
|
13
|
+
- public pages need metadata and Open Graph images
|
|
14
|
+
- generic gradient/mesh backgrounds do not replace required assets
|
|
15
|
+
- soft 3D assets must pass `soft-3d-asset-gates.md`
|
|
16
|
+
|
|
17
|
+
Check mobile framing; a hero asset that only works on desktop is not done.
|
|
18
|
+
|
|
19
|
+
## Verification
|
|
20
|
+
|
|
21
|
+
After substantial UI changes:
|
|
22
|
+
|
|
23
|
+
- build the app
|
|
24
|
+
- capture desktop and mobile screenshots
|
|
25
|
+
- verify text fitting, asset rendering, and first viewport hierarchy
|
|
26
|
+
- test loading/error/empty states
|
|
27
|
+
- verify RSC/client boundaries for interactive pieces
|
|
28
|
+
|
|
29
|
+
Report any unverified viewport or state explicitly.
|
|
30
|
+
|
|
31
|
+
## App Router (Default)
|
|
32
|
+
|
|
33
|
+
### File Conventions
|
|
34
|
+
```
|
|
35
|
+
app/
|
|
36
|
+
├── layout.tsx # Root layout (fonts, providers, metadata)
|
|
37
|
+
├── page.tsx # Home (/)
|
|
38
|
+
├── loading.tsx # Suspense fallback for this route
|
|
39
|
+
├── error.tsx # Error boundary ('use client')
|
|
40
|
+
├── not-found.tsx # 404 page
|
|
41
|
+
├── api/
|
|
42
|
+
│ └── health/route.ts # API route handler
|
|
43
|
+
└── dashboard/
|
|
44
|
+
├── layout.tsx # Nested layout
|
|
45
|
+
└── page.tsx # /dashboard
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Route Handlers
|
|
49
|
+
```tsx
|
|
50
|
+
// app/api/users/route.ts
|
|
51
|
+
export async function GET(request: Request) {
|
|
52
|
+
const users = await db.users.findMany();
|
|
53
|
+
return Response.json({ success: true, data: users });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export async function POST(request: Request) {
|
|
57
|
+
const body = await request.json();
|
|
58
|
+
const user = await db.users.create({ data: body });
|
|
59
|
+
return Response.json({ success: true, data: user }, { status: 201 });
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Server Components (Default)
|
|
66
|
+
|
|
67
|
+
Everything is a Server Component unless marked `'use client'`.
|
|
68
|
+
|
|
69
|
+
### When to use `'use client'`
|
|
70
|
+
Only for:
|
|
71
|
+
- `useState`, `useReducer`, `useEffect`
|
|
72
|
+
- Event handlers (`onClick`, `onChange`, `onSubmit`)
|
|
73
|
+
- Browser APIs (`window`, `localStorage`, `IntersectionObserver`)
|
|
74
|
+
- Third-party client libraries (Framer Motion, etc.)
|
|
75
|
+
|
|
76
|
+
### Common Mistake: Unnecessary Client Components
|
|
77
|
+
```tsx
|
|
78
|
+
// ❌ BAD — entire page is client just for one button
|
|
79
|
+
'use client';
|
|
80
|
+
export default function Page() {
|
|
81
|
+
return <div><h1>Title</h1><InteractiveButton /></div>;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ✅ GOOD — only the interactive part is client
|
|
85
|
+
export default function Page() {
|
|
86
|
+
return <div><h1>Title</h1><InteractiveButton /></div>;
|
|
87
|
+
}
|
|
88
|
+
// InteractiveButton.tsx
|
|
89
|
+
'use client';
|
|
90
|
+
export function InteractiveButton() { /* ... */ }
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Data Fetching
|
|
96
|
+
|
|
97
|
+
### Server-side (Preferred)
|
|
98
|
+
```tsx
|
|
99
|
+
// Direct database/API call in Server Components
|
|
100
|
+
async function ProductPage({ params }) {
|
|
101
|
+
const product = await getProduct(params.id);
|
|
102
|
+
return <ProductView product={product} />;
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Parallel Fetching
|
|
107
|
+
```tsx
|
|
108
|
+
async function Dashboard() {
|
|
109
|
+
const [user, stats, activity] = await Promise.all([
|
|
110
|
+
getUser(), getStats(), getActivity()
|
|
111
|
+
]);
|
|
112
|
+
return <DashboardView user={user} stats={stats} activity={activity} />;
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Streaming with Suspense
|
|
117
|
+
```tsx
|
|
118
|
+
export default function Page() {
|
|
119
|
+
return (
|
|
120
|
+
<div>
|
|
121
|
+
<Header /> {/* Renders immediately */}
|
|
122
|
+
<Suspense fallback={<DataSkeleton />}>
|
|
123
|
+
<HeavyDataSection /> {/* Streams in when ready */}
|
|
124
|
+
</Suspense>
|
|
125
|
+
</div>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Image Optimization
|
|
133
|
+
|
|
134
|
+
```tsx
|
|
135
|
+
import Image from 'next/image';
|
|
136
|
+
|
|
137
|
+
// Hero — above fold, load immediately
|
|
138
|
+
<Image src="/hero.jpg" alt="Hero" width={1200} height={600} priority />
|
|
139
|
+
|
|
140
|
+
// Responsive with fill
|
|
141
|
+
<div className="relative aspect-video">
|
|
142
|
+
<Image src="/product.jpg" alt="Product" fill
|
|
143
|
+
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
|
|
144
|
+
className="object-cover" />
|
|
145
|
+
</div>
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### next.config.js
|
|
149
|
+
```js
|
|
150
|
+
const nextConfig = {
|
|
151
|
+
images: {
|
|
152
|
+
remotePatterns: [{ hostname: 'cdn.example.com' }],
|
|
153
|
+
formats: ['image/avif', 'image/webp'],
|
|
154
|
+
},
|
|
155
|
+
experimental: {
|
|
156
|
+
optimizePackageImports: ['lucide-react', '@phosphor-icons/react'],
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Metadata & SEO
|
|
164
|
+
|
|
165
|
+
```tsx
|
|
166
|
+
// app/layout.tsx
|
|
167
|
+
export const metadata = {
|
|
168
|
+
title: { default: 'App', template: '%s | App' },
|
|
169
|
+
description: 'Description',
|
|
170
|
+
openGraph: { images: ['/og.jpg'] },
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
// Per-page
|
|
174
|
+
export const metadata = { title: 'Dashboard' };
|
|
175
|
+
|
|
176
|
+
// Dynamic
|
|
177
|
+
export async function generateMetadata({ params }) {
|
|
178
|
+
const product = await getProduct(params.id);
|
|
179
|
+
return { title: product.name, description: product.summary };
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Proxy / Middleware By Version
|
|
186
|
+
|
|
187
|
+
```tsx
|
|
188
|
+
// Next.js 16+: proxy.ts (root level)
|
|
189
|
+
import { NextResponse } from 'next/server';
|
|
190
|
+
import type { NextRequest } from 'next/server';
|
|
191
|
+
|
|
192
|
+
export function proxy(request: NextRequest) {
|
|
193
|
+
// Auth check, redirect, rewrite, etc.
|
|
194
|
+
const token = request.cookies.get('token');
|
|
195
|
+
if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
|
|
196
|
+
return NextResponse.redirect(new URL('/login', request.url));
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export const config = { matcher: ['/dashboard/:path*'] };
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
- Next.js 16+ calls this file convention **Proxy**. Use `proxy.ts` / `proxy.js` and export `proxy`.
|
|
204
|
+
- Next.js 15 and earlier use `middleware.ts` / `middleware.js` and export `middleware`.
|
|
205
|
+
- Prefer route handlers, redirects, and app-level auth checks unless request-bound proxy logic is required.
|
|
206
|
+
- Keep only one root proxy/middleware file; split route-specific logic into imported modules.
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Performance Checklist
|
|
211
|
+
|
|
212
|
+
- [ ] Server Components for all non-interactive content
|
|
213
|
+
- [ ] `priority` on above-fold images
|
|
214
|
+
- [ ] `sizes` on all responsive images
|
|
215
|
+
- [ ] Parallel data fetching (no waterfall)
|
|
216
|
+
- [ ] `Suspense` boundaries for heavy sections
|
|
217
|
+
- [ ] `optimizePackageImports` for icon libraries
|
|
218
|
+
- [ ] No barrel imports from large packages
|
|
219
|
+
- [ ] Client Components as small leaf nodes
|