@shaykec/app-agent 1.0.8 → 1.0.10

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.
Files changed (213) hide show
  1. package/.claude/agents/android-customizer.md +9 -1
  2. package/.claude/agents/catalog-analyzer.md +57 -0
  3. package/.claude/agents/ios-customizer.md +9 -1
  4. package/.claude/agents/react-native-customizer.md +71 -0
  5. package/.claude/skills/android-customizer/SKILL.md +108 -23
  6. package/.claude/skills/bug-fixer/SKILL.md +59 -0
  7. package/.claude/skills/catalog-analyzer/SKILL.md +96 -0
  8. package/.claude/skills/customization-planner/SKILL.md +44 -5
  9. package/.claude/skills/design-selector/SKILL.md +3 -1
  10. package/.claude/skills/design-system/SKILL.md +1 -1
  11. package/.claude/skills/exploratory-tester/SKILL.md +82 -0
  12. package/.claude/skills/ios-customizer/SKILL.md +123 -23
  13. package/.claude/skills/module-integrator/SKILL.md +1 -1
  14. package/.claude/skills/react-native-customizer/SKILL.md +97 -11
  15. package/.claude/skills/test-planner/SKILL.md +72 -0
  16. package/.cursor/agents/README.md +3 -1
  17. package/.cursor/agents/android-customizer.md +15 -11
  18. package/.cursor/agents/catalog-analyzer.md +83 -0
  19. package/.cursor/agents/ios-customizer.md +15 -10
  20. package/.cursor/agents/react-native-customizer.md +170 -0
  21. package/.cursor/mcp.json +2 -10
  22. package/.cursor/rules/safety-guardrails.mdc +1 -1
  23. package/.cursor/rules/workflow.mdc +52 -18
  24. package/.cursor/skills/android-customizer/SKILL.md +46 -22
  25. package/.cursor/skills/bug-fixer/SKILL.md +189 -0
  26. package/.cursor/skills/catalog-analyzer/SKILL.md +222 -0
  27. package/.cursor/skills/customization-planner/SKILL.md +55 -8
  28. package/.cursor/skills/design-selector/SKILL.md +6 -5
  29. package/.cursor/skills/design-system/SKILL.md +8 -7
  30. package/.cursor/skills/exploratory-tester/SKILL.md +223 -0
  31. package/.cursor/skills/ios-customizer/SKILL.md +50 -15
  32. package/.cursor/skills/module-integrator/SKILL.md +2 -2
  33. package/.cursor/skills/output-validator/SKILL.md +1 -1
  34. package/.cursor/skills/react-native-customizer/SKILL.md +115 -25
  35. package/.cursor/skills/test-planner/SKILL.md +199 -0
  36. package/AGENTS.md +32 -11
  37. package/CLAUDE.md +78 -33
  38. package/README.md +77 -11
  39. package/designs/DESIGN_CATALOG.md +17 -15
  40. package/designs/DESIGN_PRINCIPLES.md +53 -0
  41. package/designs/brands/accessible-high-contrast.md +14 -0
  42. package/designs/brands/corporate-professional.md +14 -0
  43. package/designs/brands/dark-luxe.md +14 -0
  44. package/designs/brands/kids-playful.md +14 -0
  45. package/designs/brands/medical-clinical.md +14 -0
  46. package/designs/brands/modern-minimal.md +14 -0
  47. package/designs/brands/nature-organic.md +14 -0
  48. package/designs/brands/neo-brutalist.md +14 -0
  49. package/designs/brands/retro-vintage.md +14 -0
  50. package/designs/brands/soft-gradient.md +14 -0
  51. package/designs/brands/sport-athletic.md +14 -0
  52. package/designs/brands/tech-dynamic.md +14 -0
  53. package/designs/brands/vibrant-playful.md +14 -0
  54. package/dist/cli.d.ts +4 -2
  55. package/dist/cli.d.ts.map +1 -1
  56. package/dist/cli.js +91 -1
  57. package/dist/cli.js.map +1 -1
  58. package/dist/config.d.ts +2 -0
  59. package/dist/config.d.ts.map +1 -1
  60. package/dist/config.js +2 -0
  61. package/dist/config.js.map +1 -1
  62. package/dist/engines/claude-engine.d.ts.map +1 -1
  63. package/dist/engines/claude-engine.js +16 -4
  64. package/dist/engines/claude-engine.js.map +1 -1
  65. package/dist/engines/types.d.ts +1 -1
  66. package/dist/engines/types.d.ts.map +1 -1
  67. package/dist/engines/types.js +31 -2
  68. package/dist/engines/types.js.map +1 -1
  69. package/dist/github.d.ts +3 -0
  70. package/dist/github.d.ts.map +1 -1
  71. package/dist/github.js +47 -4
  72. package/dist/github.js.map +1 -1
  73. package/dist/index.js +217 -9
  74. package/dist/index.js.map +1 -1
  75. package/dist/prompt-builder.d.ts +11 -1
  76. package/dist/prompt-builder.d.ts.map +1 -1
  77. package/dist/prompt-builder.js +216 -1
  78. package/dist/prompt-builder.js.map +1 -1
  79. package/dist/validator.d.ts +7 -2
  80. package/dist/validator.d.ts.map +1 -1
  81. package/dist/validator.js +61 -41
  82. package/dist/validator.js.map +1 -1
  83. package/dist/workspace.js +2 -2
  84. package/dist/workspace.js.map +1 -1
  85. package/package.json +2 -2
  86. package/prompts/agent-prompt.md +35 -18
  87. package/prompts/deep-test-agent-prompt.md +122 -0
  88. package/prompts/fix-agent-prompt.md +90 -0
  89. package/prompts/quick-agent-prompt.md +32 -2
  90. package/prompts/scratch-agent-prompt.md +5 -8
  91. package/templates/android/BookTemplate/app/src/main/kotlin/com/appship/book/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  92. package/templates/android/ChatTemplate/app/src/main/kotlin/com/appship/chat/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  93. package/templates/android/ChatTemplate/app/src/main/kotlin/com/appship/chat/features/conversations/ConversationsScreen.kt +1 -1
  94. package/templates/android/DashTemplate/app/src/main/kotlin/com/appship/dash/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  95. package/templates/android/DashTemplate/app/src/main/kotlin/com/appship/dash/features/navigation/MainScreen.kt +1 -0
  96. package/templates/android/FamilyTemplate/app/src/main/java/com/appship/family/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  97. package/templates/android/FamilyTemplate/app/src/main/java/com/appship/family/features/navigation/MainNavigation.kt +5 -1
  98. package/templates/android/FinanceTemplate/app/src/main/kotlin/com/appship/finance/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  99. package/templates/android/GameTemplate/app/src/main/kotlin/com/appship/game/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  100. package/templates/android/GameTemplate/app/src/main/kotlin/com/appship/game/core/animation/MotionPreferencesScreen.kt +3 -3
  101. package/templates/android/GameTemplate/app/src/main/kotlin/com/appship/game/features/navigation/Navigation.kt +1 -1
  102. package/templates/android/GameTemplate/app/src/main/kotlin/com/appship/game/features/settings/SettingsScreen.kt +1 -1
  103. package/templates/android/HealthTemplate/app/src/main/kotlin/com/appship/health/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  104. package/templates/android/LearnTemplate/app/src/main/kotlin/com/appship/learn/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  105. package/templates/android/MapTemplate/app/src/main/kotlin/com/appship/map/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  106. package/templates/android/MediaTemplate/app/src/main/kotlin/com/appship/media/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  107. package/templates/android/MediaTemplate/app/src/main/kotlin/com/appship/media/features/settings/SettingsScreen.kt +3 -2
  108. package/templates/android/ReferenceTemplate/app/src/main/kotlin/com/appship/reference/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  109. package/templates/android/ReferenceTemplate/app/src/main/kotlin/com/appship/reference/features/settings/SettingsScreen.kt +1 -1
  110. package/templates/android/ShopTemplate/app/src/main/kotlin/com/appship/shop/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  111. package/templates/android/ShopTemplate/app/src/main/kotlin/com/appship/shop/features/cart/CartScreen.kt +3 -2
  112. package/templates/android/Skeleton/TESTING_MANIFEST.md +2 -1
  113. package/templates/android/Skeleton/app/src/main/kotlin/com/appship/skeleton/MainActivity.kt +23 -2
  114. package/templates/android/Skeleton/app/src/main/kotlin/com/appship/skeleton/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  115. package/templates/android/Skeleton/app/src/main/kotlin/com/appship/skeleton/core/theme/AppearanceManager.kt +42 -0
  116. package/templates/android/Skeleton/app/src/main/kotlin/com/appship/skeleton/features/profile/ProfileScreen.kt +20 -8
  117. package/templates/android/Skeleton/tests/03_detail_screen.yaml +3 -2
  118. package/templates/android/Skeleton/tests/04_favorites.yaml +3 -2
  119. package/templates/android/Skeleton/tests/08_full_e2e.yaml +9 -2
  120. package/templates/android/Skeleton/tests/09_dark_mode.yaml +50 -0
  121. package/templates/android/SocialTemplate/app/src/main/kotlin/com/appship/social/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  122. package/templates/android/TaskTemplate/app/src/main/kotlin/com/appship/task/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  123. package/templates/android/TaskTemplate/app/src/main/kotlin/com/appship/task/features/settings/SettingsScreen.kt +3 -2
  124. package/templates/android/TrackTemplate/app/src/main/kotlin/com/appship/track/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  125. package/templates/ios/BookTemplate/BookTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  126. package/templates/ios/ChatTemplate/ChatTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  127. package/templates/ios/DashTemplate/DashTemplate/App/AppConfig.swift +1 -0
  128. package/templates/ios/DashTemplate/DashTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  129. package/templates/ios/DashTemplate/DashTemplate/Core/Strings.swift +13 -0
  130. package/templates/ios/DashTemplate/DashTemplate.xcodeproj/project.pbxproj +32 -20
  131. package/templates/ios/FamilyTemplate/FamilyTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  132. package/templates/ios/FinanceTemplate/FinanceTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  133. package/templates/ios/FinanceTemplate/FinanceTemplate/Core/Strings.swift +42 -0
  134. package/templates/ios/FinanceTemplate/FinanceTemplate.xcodeproj/project.pbxproj +36 -30
  135. package/templates/ios/GameTemplate/GameTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  136. package/templates/ios/HealthTemplate/HealthTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  137. package/templates/ios/LearnTemplate/LearnTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  138. package/templates/ios/MapTemplate/MapTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  139. package/templates/ios/MediaTemplate/MediaTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  140. package/templates/ios/ReferenceTemplate/ReferenceTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  141. package/templates/ios/ReferenceTemplate/ReferenceTemplate/Core/Strings.swift +12 -0
  142. package/templates/ios/ReferenceTemplate/ReferenceTemplate/Features/SkeletonLoading/SkeletonLoadingView.swift +2 -37
  143. package/templates/ios/ShopTemplate/ShopTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  144. package/templates/ios/Skeleton/Skeleton/Core/Animation/AnimatedTransitionsView.swift +201 -0
  145. package/templates/ios/Skeleton/tests/08_full_e2e.yaml +4 -0
  146. package/templates/ios/Skeleton/tests/09_dark_mode.yaml +52 -0
  147. package/templates/ios/SocialTemplate/SocialTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  148. package/templates/ios/TaskTemplate/TaskTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  149. package/templates/ios/TrackTemplate/TrackTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  150. package/templates/react-native/BookTemplate/src/animation/useAnimatedList.ts +219 -2
  151. package/templates/react-native/BookTemplate/src/animation/useMotionPreferences.ts +23 -9
  152. package/templates/react-native/BookTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
  153. package/templates/react-native/ChatTemplate/src/animation/useAnimatedList.ts +219 -2
  154. package/templates/react-native/ChatTemplate/src/animation/useMotionPreferences.ts +23 -9
  155. package/templates/react-native/ChatTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
  156. package/templates/react-native/DashTemplate/src/animation/useAnimatedList.ts +219 -2
  157. package/templates/react-native/DashTemplate/src/animation/useMotionPreferences.ts +23 -9
  158. package/templates/react-native/DashTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
  159. package/templates/react-native/FamilyTemplate/src/animation/useAnimatedList.ts +219 -2
  160. package/templates/react-native/FamilyTemplate/src/animation/useMotionPreferences.ts +23 -9
  161. package/templates/react-native/FamilyTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
  162. package/templates/react-native/FinanceTemplate/src/animation/useAnimatedList.ts +219 -2
  163. package/templates/react-native/FinanceTemplate/src/animation/useMotionPreferences.ts +23 -9
  164. package/templates/react-native/FinanceTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
  165. package/templates/react-native/GameTemplate/src/animation/useAnimatedList.ts +219 -2
  166. package/templates/react-native/GameTemplate/src/animation/useMotionPreferences.ts +23 -9
  167. package/templates/react-native/GameTemplate/src/screens/GameDetail/GameDetailScreen.tsx +2 -1
  168. package/templates/react-native/GameTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
  169. package/templates/react-native/HealthTemplate/src/animation/useAnimatedList.ts +219 -2
  170. package/templates/react-native/HealthTemplate/src/animation/useMotionPreferences.ts +23 -9
  171. package/templates/react-native/HealthTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
  172. package/templates/react-native/HealthTemplate/src/screens/WorkoutDetail/WorkoutDetailScreen.tsx +1 -1
  173. package/templates/react-native/LearnTemplate/src/animation/useAnimatedList.ts +219 -2
  174. package/templates/react-native/LearnTemplate/src/animation/useMotionPreferences.ts +23 -9
  175. package/templates/react-native/LearnTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
  176. package/templates/react-native/MapTemplate/src/animation/useAnimatedList.ts +219 -2
  177. package/templates/react-native/MapTemplate/src/animation/useMotionPreferences.ts +23 -9
  178. package/templates/react-native/MapTemplate/src/screens/Map/MapScreen.tsx +14 -0
  179. package/templates/react-native/MapTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
  180. package/templates/react-native/MediaTemplate/src/animation/useAnimatedList.ts +219 -2
  181. package/templates/react-native/MediaTemplate/src/animation/useMotionPreferences.ts +23 -9
  182. package/templates/react-native/MediaTemplate/src/screens/PlaylistDetail/PlaylistDetailScreen.tsx +1 -1
  183. package/templates/react-native/MediaTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
  184. package/templates/react-native/ReferenceTemplate/src/animation/useAnimatedList.ts +219 -2
  185. package/templates/react-native/ReferenceTemplate/src/animation/useMotionPreferences.ts +23 -9
  186. package/templates/react-native/ReferenceTemplate/src/screens/Settings/SettingsScreen.tsx +1 -1
  187. package/templates/react-native/ShopTemplate/src/animation/useAnimatedList.ts +219 -2
  188. package/templates/react-native/ShopTemplate/src/animation/useMotionPreferences.ts +23 -9
  189. package/templates/react-native/ShopTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
  190. package/templates/react-native/Skeleton/TESTING_MANIFEST.md +2 -1
  191. package/templates/react-native/Skeleton/src/animation/useAnimatedList.ts +219 -2
  192. package/templates/react-native/Skeleton/src/animation/useMotionPreferences.ts +23 -9
  193. package/templates/react-native/Skeleton/src/screens/Profile/ProfileScreen.tsx +1 -1
  194. package/templates/react-native/Skeleton/tests/07_profile.yaml +3 -2
  195. package/templates/react-native/Skeleton/tests/08_full_e2e.yaml +12 -1
  196. package/templates/react-native/Skeleton/tests/09_dark_mode.yaml +46 -0
  197. package/templates/react-native/SocialTemplate/src/animation/useAnimatedList.ts +219 -2
  198. package/templates/react-native/SocialTemplate/src/animation/useMotionPreferences.ts +23 -9
  199. package/templates/react-native/SocialTemplate/src/screens/Feed/FeedScreen.tsx +1 -0
  200. package/templates/react-native/SocialTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
  201. package/templates/react-native/TaskTemplate/src/animation/useAnimatedList.ts +219 -2
  202. package/templates/react-native/TaskTemplate/src/animation/useMotionPreferences.ts +23 -9
  203. package/templates/react-native/TaskTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
  204. package/templates/react-native/TrackTemplate/src/animation/useAnimatedList.ts +219 -2
  205. package/templates/react-native/TrackTemplate/src/animation/useMotionPreferences.ts +23 -9
  206. package/templates/react-native/TrackTemplate/src/screens/Settings/SettingsScreen.tsx +1 -1
  207. package/templates/shared/ios/AnimatedTransitions/AnimatedTransitionsView.swift +233 -93
  208. package/.claude/agents/template-selector.md +0 -39
  209. package/.claude/skills/module-selector/SKILL.md +0 -81
  210. package/.claude/skills/template-selector/SKILL.md +0 -44
  211. package/.cursor/agents/template-selector.md +0 -52
  212. package/.cursor/skills/module-selector/SKILL.md +0 -135
  213. package/.cursor/skills/template-selector/SKILL.md +0 -123
@@ -1,14 +1,24 @@
1
1
  package com.appship.track.core.animation
2
2
 
3
+ import android.view.HapticFeedbackConstants
3
4
  import androidx.compose.animation.*
4
5
  import androidx.compose.animation.core.*
6
+ import androidx.compose.foundation.clickable
7
+ import androidx.compose.foundation.gestures.detectTapGestures
8
+ import androidx.compose.foundation.interaction.MutableInteractionSource
9
+ import androidx.compose.foundation.interaction.collectIsPressedAsState
5
10
  import androidx.compose.foundation.layout.offset
6
11
  import androidx.compose.runtime.*
7
12
  import androidx.compose.ui.Modifier
8
13
  import androidx.compose.ui.composed
9
14
  import androidx.compose.ui.draw.alpha
10
15
  import androidx.compose.ui.draw.scale
16
+ import androidx.compose.ui.draw.shadow
17
+ import androidx.compose.ui.graphics.Color
18
+ import androidx.compose.ui.input.pointer.pointerInput
19
+ import androidx.compose.ui.platform.LocalView
11
20
  import androidx.compose.ui.unit.IntOffset
21
+ import androidx.compose.ui.unit.dp
12
22
 
13
23
  /**
14
24
  * Staggered appear animation for list items.
@@ -118,3 +128,181 @@ fun Modifier.bounceOnChange(
118
128
 
119
129
  this.scale(bounceAnim)
120
130
  }
131
+
132
+ /**
133
+ * Scale-down press feedback for tappable elements.
134
+ * Scales to 0.95 on press with spring return animation.
135
+ */
136
+ fun Modifier.scaleOnPress(): Modifier = composed {
137
+ val interactionSource = remember { MutableInteractionSource() }
138
+ val isPressed by interactionSource.collectIsPressedAsState()
139
+
140
+ val scale by animateFloatAsState(
141
+ targetValue = if (isPressed) 0.95f else 1f,
142
+ animationSpec = spring(
143
+ dampingRatio = Spring.DampingRatioMediumBouncy,
144
+ stiffness = Spring.StiffnessMedium
145
+ ),
146
+ label = "pressScale"
147
+ )
148
+
149
+ this
150
+ .scale(scale)
151
+ .clickable(
152
+ interactionSource = interactionSource,
153
+ indication = null
154
+ ) { }
155
+ }
156
+
157
+ /**
158
+ * Triggers haptic feedback on tap.
159
+ */
160
+ fun Modifier.hapticFeedback(): Modifier = composed {
161
+ val view = LocalView.current
162
+ this.pointerInput(Unit) {
163
+ detectTapGestures {
164
+ view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
165
+ }
166
+ }
167
+ }
168
+
169
+ /**
170
+ * Slide-from-side + fade-in animation on appear.
171
+ */
172
+ fun Modifier.slideAndFade(
173
+ delayMs: Int = 0,
174
+ durationMs: Int = 400
175
+ ): Modifier = composed {
176
+ var isVisible by remember { mutableStateOf(false) }
177
+
178
+ val alpha by animateFloatAsState(
179
+ targetValue = if (isVisible) 1f else 0f,
180
+ animationSpec = tween(
181
+ durationMillis = durationMs,
182
+ delayMillis = delayMs,
183
+ easing = FastOutSlowInEasing
184
+ ),
185
+ label = "slideAndFadeAlpha"
186
+ )
187
+
188
+ val offsetX by animateIntAsState(
189
+ targetValue = if (isVisible) 0 else 40,
190
+ animationSpec = tween(
191
+ durationMillis = durationMs,
192
+ delayMillis = delayMs,
193
+ easing = FastOutSlowInEasing
194
+ ),
195
+ label = "slideAndFadeOffsetX"
196
+ )
197
+
198
+ LaunchedEffect(Unit) {
199
+ isVisible = true
200
+ }
201
+
202
+ this
203
+ .alpha(alpha)
204
+ .offset { IntOffset(offsetX, 0) }
205
+ }
206
+
207
+ /**
208
+ * Subtle colored shadow for cards and elevated surfaces.
209
+ * Uses Material 3 elevation with optional color tinting.
210
+ */
211
+ fun Modifier.cardShadow(
212
+ color: Color = Color.Black,
213
+ elevation: Float = 4f
214
+ ): Modifier = composed {
215
+ this.shadow(
216
+ elevation = elevation.dp,
217
+ ambientColor = color.copy(alpha = 0.08f),
218
+ spotColor = color.copy(alpha = 0.12f),
219
+ shape = androidx.compose.foundation.shape.RoundedCornerShape(12.dp)
220
+ )
221
+ }
222
+
223
+ /**
224
+ * Deeper shadow for floating elements (FABs, modals).
225
+ */
226
+ fun Modifier.elevatedShadow(
227
+ color: Color = Color.Black
228
+ ): Modifier = composed {
229
+ this.shadow(
230
+ elevation = 8.dp,
231
+ ambientColor = color.copy(alpha = 0.12f),
232
+ spotColor = color.copy(alpha = 0.16f),
233
+ shape = androidx.compose.foundation.shape.RoundedCornerShape(16.dp)
234
+ )
235
+ }
236
+
237
+ /**
238
+ * Shimmer loading overlay for skeleton placeholder views.
239
+ * Sweeps a translucent gradient across the content repeatedly.
240
+ */
241
+ fun Modifier.shimmer(
242
+ durationMs: Int = 1500
243
+ ): Modifier = composed {
244
+ val transition = rememberInfiniteTransition(label = "shimmer")
245
+ val translateAnim by transition.animateFloat(
246
+ initialValue = -1f,
247
+ targetValue = 2f,
248
+ animationSpec = infiniteRepeatable(
249
+ animation = tween(durationMillis = durationMs, easing = LinearEasing),
250
+ repeatMode = RepeatMode.Restart
251
+ ),
252
+ label = "shimmerTranslate"
253
+ )
254
+
255
+ this.alpha(0.3f + 0.7f * ((translateAnim + 1f) / 3f).coerceIn(0f, 1f))
256
+ }
257
+
258
+ /**
259
+ * Repeating scale pulse animation for live indicators and notifications.
260
+ */
261
+ fun Modifier.pulse(
262
+ intensity: Float = 0.05f,
263
+ durationMs: Int = 1000
264
+ ): Modifier = composed {
265
+ val transition = rememberInfiniteTransition(label = "pulse")
266
+ val scale by transition.animateFloat(
267
+ initialValue = 1f,
268
+ targetValue = 1f + intensity,
269
+ animationSpec = infiniteRepeatable(
270
+ animation = tween(durationMillis = durationMs, easing = FastOutSlowInEasing),
271
+ repeatMode = RepeatMode.Reverse
272
+ ),
273
+ label = "pulseScale"
274
+ )
275
+
276
+ this.scale(scale)
277
+ }
278
+
279
+ /**
280
+ * Heart/like bounce effect when toggled active.
281
+ * Scales up to 1.3x then springs back. Perfect for favorite buttons.
282
+ */
283
+ fun Modifier.heartBounce(
284
+ isActive: Boolean
285
+ ): Modifier = composed {
286
+ var previousActive by remember { mutableStateOf(isActive) }
287
+ var animatedScale by remember { mutableFloatStateOf(1f) }
288
+
289
+ val bounceAnim by animateFloatAsState(
290
+ targetValue = animatedScale,
291
+ animationSpec = spring(
292
+ dampingRatio = Spring.DampingRatioMediumBouncy,
293
+ stiffness = Spring.StiffnessMedium
294
+ ),
295
+ label = "heartBounce"
296
+ )
297
+
298
+ LaunchedEffect(isActive) {
299
+ if (isActive && !previousActive) {
300
+ animatedScale = 1.3f
301
+ kotlinx.coroutines.delay(200)
302
+ animatedScale = 1f
303
+ }
304
+ previousActive = isActive
305
+ }
306
+
307
+ this.scale(bounceAnim)
308
+ }
@@ -57,6 +57,155 @@ struct BounceScaleModifier: ViewModifier {
57
57
  }
58
58
  }
59
59
 
60
+ // MARK: - Scale on Press Modifier
61
+ /// Applies scale-down + spring-back animation when pressed (like a button press).
62
+ struct ScaleOnPressModifier: ViewModifier {
63
+ @State private var isPressed = false
64
+
65
+ func body(content: Content) -> some View {
66
+ content
67
+ .scaleEffect(isPressed ? 0.95 : 1.0)
68
+ .animation(.spring(response: 0.3, dampingFraction: 0.7), value: isPressed)
69
+ .simultaneousGesture(
70
+ DragGesture(minimumDistance: 0)
71
+ .onChanged { _ in isPressed = true }
72
+ .onEnded { _ in isPressed = false }
73
+ )
74
+ }
75
+ }
76
+
77
+ // MARK: - Haptic Feedback Modifier
78
+ /// Triggers haptic feedback on tap.
79
+ struct HapticFeedbackModifier: ViewModifier {
80
+ let style: UIImpactFeedbackGenerator.FeedbackStyle
81
+
82
+ func body(content: Content) -> some View {
83
+ content
84
+ .onTapGesture {
85
+ UIImpactFeedbackGenerator(style: style).impactOccurred()
86
+ }
87
+ }
88
+ }
89
+
90
+ // MARK: - Slide and Fade Modifier
91
+ /// Applies a slide-from-side + fade-in animation on appear.
92
+ struct SlideAndFadeModifier: ViewModifier {
93
+ let delay: Double
94
+ @State private var isVisible = false
95
+
96
+ func body(content: Content) -> some View {
97
+ content
98
+ .opacity(isVisible ? 1 : 0)
99
+ .offset(x: isVisible ? 0 : 20)
100
+ .motionAware(animation: .spring(response: 0.5, dampingFraction: 0.8).delay(delay))
101
+ .onAppear {
102
+ isVisible = true
103
+ }
104
+ }
105
+ }
106
+
107
+ // MARK: - Card Shadow Modifier
108
+ /// Applies a subtle colored shadow to cards and elevated surfaces.
109
+ struct CardShadowModifier: ViewModifier {
110
+ let color: Color
111
+ let radius: CGFloat
112
+ let y: CGFloat
113
+
114
+ func body(content: Content) -> some View {
115
+ content
116
+ .shadow(color: color.opacity(0.08), radius: radius, x: 0, y: y)
117
+ .shadow(color: Color.black.opacity(0.04), radius: radius / 2, x: 0, y: y / 2)
118
+ }
119
+ }
120
+
121
+ // MARK: - Elevated Shadow Modifier
122
+ /// Applies a deeper shadow for FABs and floating elements.
123
+ struct ElevatedShadowModifier: ViewModifier {
124
+ let color: Color
125
+
126
+ func body(content: Content) -> some View {
127
+ content
128
+ .shadow(color: color.opacity(0.12), radius: 16, x: 0, y: 8)
129
+ .shadow(color: Color.black.opacity(0.06), radius: 8, x: 0, y: 4)
130
+ }
131
+ }
132
+
133
+ // MARK: - Shimmer Modifier
134
+ /// Applies a shimmer/skeleton-loading gradient overlay for loading states.
135
+ struct ShimmerModifier: ViewModifier {
136
+ @State private var isAnimating = false
137
+
138
+ func body(content: Content) -> some View {
139
+ content
140
+ .overlay(
141
+ GeometryReader { geometry in
142
+ if isAnimating {
143
+ LinearGradient(
144
+ colors: [
145
+ Color.clear,
146
+ Color.white.opacity(0.3),
147
+ Color.clear
148
+ ],
149
+ startPoint: .leading,
150
+ endPoint: .trailing
151
+ )
152
+ .offset(x: isAnimating ? geometry.size.width : -geometry.size.width)
153
+ .animation(
154
+ Animation.linear(duration: 1.5)
155
+ .repeatForever(autoreverses: false),
156
+ value: isAnimating
157
+ )
158
+ }
159
+ }
160
+ )
161
+ .clipped()
162
+ .onAppear {
163
+ isAnimating = true
164
+ }
165
+ }
166
+ }
167
+
168
+ // MARK: - Pulse Modifier
169
+ /// Applies a repeating scale pulse effect (e.g., for notifications, active indicators).
170
+ struct PulseModifier: ViewModifier {
171
+ let intensity: CGFloat
172
+ @State private var scale: CGFloat = 1.0
173
+
174
+ func body(content: Content) -> some View {
175
+ content
176
+ .scaleEffect(scale)
177
+ .onAppear {
178
+ withAnimation(
179
+ .easeInOut(duration: 1.0)
180
+ .repeatForever(autoreverses: true)
181
+ ) {
182
+ scale = 1.0 + intensity
183
+ }
184
+ }
185
+ }
186
+ }
187
+
188
+ // MARK: - Heart Bounce Modifier
189
+ /// Applies a scale-up bounce then return when toggled (e.g., favorite/like buttons).
190
+ struct HeartBounceModifier: ViewModifier {
191
+ let isActive: Bool
192
+ @State private var scale: CGFloat = 1.0
193
+
194
+ func body(content: Content) -> some View {
195
+ content
196
+ .scaleEffect(scale)
197
+ .motionAware(animation: .spring(response: 0.3, dampingFraction: 0.5))
198
+ .onChange(of: isActive) { _, newValue in
199
+ if newValue {
200
+ scale = 1.3
201
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
202
+ scale = 1.0
203
+ }
204
+ }
205
+ }
206
+ }
207
+ }
208
+
60
209
  // MARK: - View Extensions
61
210
 
62
211
  extension View {
@@ -79,4 +228,56 @@ extension View {
79
228
  func bounceOnChange(isActive: Bool) -> some View {
80
229
  modifier(BounceScaleModifier(isActive: isActive))
81
230
  }
231
+
232
+ /// Applies scale-down press feedback to any tappable element.
233
+ /// Scales to 0.95 on press with spring return animation.
234
+ func scaleOnPress() -> some View {
235
+ modifier(ScaleOnPressModifier())
236
+ }
237
+
238
+ /// Triggers haptic feedback on tap.
239
+ /// - Parameter style: Feedback intensity (.light, .medium, .heavy)
240
+ func hapticFeedback(_ style: UIImpactFeedbackGenerator.FeedbackStyle = .light) -> some View {
241
+ modifier(HapticFeedbackModifier(style: style))
242
+ }
243
+
244
+ /// Applies a slide-from-side + fade-in animation on appear.
245
+ /// - Parameter delay: Delay before animation starts (default: 0)
246
+ func slideAndFade(delay: Double = 0) -> some View {
247
+ modifier(SlideAndFadeModifier(delay: delay))
248
+ }
249
+
250
+ /// Applies subtle colored shadow to cards and elevated surfaces.
251
+ /// Uses dual-shadow technique: primary-tinted shadow + neutral shadow for depth.
252
+ /// - Parameters:
253
+ /// - color: Shadow tint color (default: .primary)
254
+ /// - radius: Shadow blur radius (default: 8)
255
+ /// - y: Vertical offset (default: 4)
256
+ func cardShadow(color: Color = .primary, radius: CGFloat = 8, y: CGFloat = 4) -> some View {
257
+ modifier(CardShadowModifier(color: color, radius: radius, y: y))
258
+ }
259
+
260
+ /// Applies deeper shadow for floating elements (FABs, modals).
261
+ /// - Parameter color: Shadow tint color (default: .primary)
262
+ func elevatedShadow(color: Color = .primary) -> some View {
263
+ modifier(ElevatedShadowModifier(color: color))
264
+ }
265
+
266
+ /// Applies a shimmer loading overlay (e.g., for skeleton placeholder views).
267
+ func shimmer() -> some View {
268
+ modifier(ShimmerModifier())
269
+ }
270
+
271
+ /// Applies a repeating pulse animation (e.g., for notifications, live indicators).
272
+ /// - Parameter intensity: Scale increase per pulse (default: 0.05 = 5% bigger)
273
+ func pulse(intensity: CGFloat = 0.05) -> some View {
274
+ modifier(PulseModifier(intensity: intensity))
275
+ }
276
+
277
+ /// Applies a heart/like bounce effect when toggled active.
278
+ /// Scales up to 1.3x then springs back. Perfect for favorite buttons.
279
+ /// - Parameter isActive: Boolean that triggers the bounce when it becomes true
280
+ func heartBounce(isActive: Bool) -> some View {
281
+ modifier(HeartBounceModifier(isActive: isActive))
282
+ }
82
283
  }
@@ -57,6 +57,155 @@ struct BounceScaleModifier: ViewModifier {
57
57
  }
58
58
  }
59
59
 
60
+ // MARK: - Scale on Press Modifier
61
+ /// Applies scale-down + spring-back animation when pressed (like a button press).
62
+ struct ScaleOnPressModifier: ViewModifier {
63
+ @State private var isPressed = false
64
+
65
+ func body(content: Content) -> some View {
66
+ content
67
+ .scaleEffect(isPressed ? 0.95 : 1.0)
68
+ .animation(.spring(response: 0.3, dampingFraction: 0.7), value: isPressed)
69
+ .simultaneousGesture(
70
+ DragGesture(minimumDistance: 0)
71
+ .onChanged { _ in isPressed = true }
72
+ .onEnded { _ in isPressed = false }
73
+ )
74
+ }
75
+ }
76
+
77
+ // MARK: - Haptic Feedback Modifier
78
+ /// Triggers haptic feedback on tap.
79
+ struct HapticFeedbackModifier: ViewModifier {
80
+ let style: UIImpactFeedbackGenerator.FeedbackStyle
81
+
82
+ func body(content: Content) -> some View {
83
+ content
84
+ .onTapGesture {
85
+ UIImpactFeedbackGenerator(style: style).impactOccurred()
86
+ }
87
+ }
88
+ }
89
+
90
+ // MARK: - Slide and Fade Modifier
91
+ /// Applies a slide-from-side + fade-in animation on appear.
92
+ struct SlideAndFadeModifier: ViewModifier {
93
+ let delay: Double
94
+ @State private var isVisible = false
95
+
96
+ func body(content: Content) -> some View {
97
+ content
98
+ .opacity(isVisible ? 1 : 0)
99
+ .offset(x: isVisible ? 0 : 20)
100
+ .motionAware(animation: .spring(response: 0.5, dampingFraction: 0.8).delay(delay))
101
+ .onAppear {
102
+ isVisible = true
103
+ }
104
+ }
105
+ }
106
+
107
+ // MARK: - Card Shadow Modifier
108
+ /// Applies a subtle colored shadow to cards and elevated surfaces.
109
+ struct CardShadowModifier: ViewModifier {
110
+ let color: Color
111
+ let radius: CGFloat
112
+ let y: CGFloat
113
+
114
+ func body(content: Content) -> some View {
115
+ content
116
+ .shadow(color: color.opacity(0.08), radius: radius, x: 0, y: y)
117
+ .shadow(color: Color.black.opacity(0.04), radius: radius / 2, x: 0, y: y / 2)
118
+ }
119
+ }
120
+
121
+ // MARK: - Elevated Shadow Modifier
122
+ /// Applies a deeper shadow for FABs and floating elements.
123
+ struct ElevatedShadowModifier: ViewModifier {
124
+ let color: Color
125
+
126
+ func body(content: Content) -> some View {
127
+ content
128
+ .shadow(color: color.opacity(0.12), radius: 16, x: 0, y: 8)
129
+ .shadow(color: Color.black.opacity(0.06), radius: 8, x: 0, y: 4)
130
+ }
131
+ }
132
+
133
+ // MARK: - Shimmer Modifier
134
+ /// Applies a shimmer/skeleton-loading gradient overlay for loading states.
135
+ struct ShimmerModifier: ViewModifier {
136
+ @State private var isAnimating = false
137
+
138
+ func body(content: Content) -> some View {
139
+ content
140
+ .overlay(
141
+ GeometryReader { geometry in
142
+ if isAnimating {
143
+ LinearGradient(
144
+ colors: [
145
+ Color.clear,
146
+ Color.white.opacity(0.3),
147
+ Color.clear
148
+ ],
149
+ startPoint: .leading,
150
+ endPoint: .trailing
151
+ )
152
+ .offset(x: isAnimating ? geometry.size.width : -geometry.size.width)
153
+ .animation(
154
+ Animation.linear(duration: 1.5)
155
+ .repeatForever(autoreverses: false),
156
+ value: isAnimating
157
+ )
158
+ }
159
+ }
160
+ )
161
+ .clipped()
162
+ .onAppear {
163
+ isAnimating = true
164
+ }
165
+ }
166
+ }
167
+
168
+ // MARK: - Pulse Modifier
169
+ /// Applies a repeating scale pulse effect (e.g., for notifications, active indicators).
170
+ struct PulseModifier: ViewModifier {
171
+ let intensity: CGFloat
172
+ @State private var scale: CGFloat = 1.0
173
+
174
+ func body(content: Content) -> some View {
175
+ content
176
+ .scaleEffect(scale)
177
+ .onAppear {
178
+ withAnimation(
179
+ .easeInOut(duration: 1.0)
180
+ .repeatForever(autoreverses: true)
181
+ ) {
182
+ scale = 1.0 + intensity
183
+ }
184
+ }
185
+ }
186
+ }
187
+
188
+ // MARK: - Heart Bounce Modifier
189
+ /// Applies a scale-up bounce then return when toggled (e.g., favorite/like buttons).
190
+ struct HeartBounceModifier: ViewModifier {
191
+ let isActive: Bool
192
+ @State private var scale: CGFloat = 1.0
193
+
194
+ func body(content: Content) -> some View {
195
+ content
196
+ .scaleEffect(scale)
197
+ .motionAware(animation: .spring(response: 0.3, dampingFraction: 0.5))
198
+ .onChange(of: isActive) { _, newValue in
199
+ if newValue {
200
+ scale = 1.3
201
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
202
+ scale = 1.0
203
+ }
204
+ }
205
+ }
206
+ }
207
+ }
208
+
60
209
  // MARK: - View Extensions
61
210
 
62
211
  extension View {
@@ -79,4 +228,56 @@ extension View {
79
228
  func bounceOnChange(isActive: Bool) -> some View {
80
229
  modifier(BounceScaleModifier(isActive: isActive))
81
230
  }
231
+
232
+ /// Applies scale-down press feedback to any tappable element.
233
+ /// Scales to 0.95 on press with spring return animation.
234
+ func scaleOnPress() -> some View {
235
+ modifier(ScaleOnPressModifier())
236
+ }
237
+
238
+ /// Triggers haptic feedback on tap.
239
+ /// - Parameter style: Feedback intensity (.light, .medium, .heavy)
240
+ func hapticFeedback(_ style: UIImpactFeedbackGenerator.FeedbackStyle = .light) -> some View {
241
+ modifier(HapticFeedbackModifier(style: style))
242
+ }
243
+
244
+ /// Applies a slide-from-side + fade-in animation on appear.
245
+ /// - Parameter delay: Delay before animation starts (default: 0)
246
+ func slideAndFade(delay: Double = 0) -> some View {
247
+ modifier(SlideAndFadeModifier(delay: delay))
248
+ }
249
+
250
+ /// Applies subtle colored shadow to cards and elevated surfaces.
251
+ /// Uses dual-shadow technique: primary-tinted shadow + neutral shadow for depth.
252
+ /// - Parameters:
253
+ /// - color: Shadow tint color (default: .primary)
254
+ /// - radius: Shadow blur radius (default: 8)
255
+ /// - y: Vertical offset (default: 4)
256
+ func cardShadow(color: Color = .primary, radius: CGFloat = 8, y: CGFloat = 4) -> some View {
257
+ modifier(CardShadowModifier(color: color, radius: radius, y: y))
258
+ }
259
+
260
+ /// Applies deeper shadow for floating elements (FABs, modals).
261
+ /// - Parameter color: Shadow tint color (default: .primary)
262
+ func elevatedShadow(color: Color = .primary) -> some View {
263
+ modifier(ElevatedShadowModifier(color: color))
264
+ }
265
+
266
+ /// Applies a shimmer loading overlay (e.g., for skeleton placeholder views).
267
+ func shimmer() -> some View {
268
+ modifier(ShimmerModifier())
269
+ }
270
+
271
+ /// Applies a repeating pulse animation (e.g., for notifications, live indicators).
272
+ /// - Parameter intensity: Scale increase per pulse (default: 0.05 = 5% bigger)
273
+ func pulse(intensity: CGFloat = 0.05) -> some View {
274
+ modifier(PulseModifier(intensity: intensity))
275
+ }
276
+
277
+ /// Applies a heart/like bounce effect when toggled active.
278
+ /// Scales up to 1.3x then springs back. Perfect for favorite buttons.
279
+ /// - Parameter isActive: Boolean that triggers the bounce when it becomes true
280
+ func heartBounce(isActive: Bool) -> some View {
281
+ modifier(HeartBounceModifier(isActive: isActive))
282
+ }
82
283
  }
@@ -16,6 +16,7 @@ struct AppConfig {
16
16
  static let enableDrillDown = true
17
17
  static let enableDataSources = true
18
18
  static let enableDarkModeDefault = false
19
+ static let enableAnimations = true // CUSTOMIZE:ANIMATIONS - Motion preferences & animated transitions
19
20
  }
20
21
 
21
22
  // CUSTOMIZE:API