@shaykec/app-agent 1.0.9 → 1.0.11

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 (202) hide show
  1. package/.claude/agents/catalog-analyzer.md +57 -0
  2. package/.claude/skills/android-customizer/SKILL.md +23 -10
  3. package/.claude/skills/bug-fixer/SKILL.md +59 -0
  4. package/.claude/skills/catalog-analyzer/SKILL.md +96 -0
  5. package/.claude/skills/customization-planner/SKILL.md +44 -5
  6. package/.claude/skills/design-selector/SKILL.md +3 -1
  7. package/.claude/skills/design-system/SKILL.md +1 -1
  8. package/.claude/skills/exploratory-tester/SKILL.md +82 -0
  9. package/.claude/skills/ios-customizer/SKILL.md +29 -8
  10. package/.claude/skills/module-integrator/SKILL.md +1 -1
  11. package/.claude/skills/react-native-customizer/SKILL.md +22 -10
  12. package/.claude/skills/test-planner/SKILL.md +72 -0
  13. package/.cursor/agents/README.md +3 -1
  14. package/.cursor/agents/catalog-analyzer.md +83 -0
  15. package/.cursor/rules/safety-guardrails.mdc +1 -1
  16. package/.cursor/rules/workflow.mdc +52 -18
  17. package/.cursor/skills/android-customizer/SKILL.md +43 -19
  18. package/.cursor/skills/bug-fixer/SKILL.md +189 -0
  19. package/.cursor/skills/catalog-analyzer/SKILL.md +222 -0
  20. package/.cursor/skills/customization-planner/SKILL.md +55 -8
  21. package/.cursor/skills/design-selector/SKILL.md +6 -5
  22. package/.cursor/skills/design-system/SKILL.md +8 -7
  23. package/.cursor/skills/exploratory-tester/SKILL.md +223 -0
  24. package/.cursor/skills/ios-customizer/SKILL.md +47 -12
  25. package/.cursor/skills/module-integrator/SKILL.md +2 -2
  26. package/.cursor/skills/output-validator/SKILL.md +1 -1
  27. package/.cursor/skills/react-native-customizer/SKILL.md +46 -16
  28. package/.cursor/skills/test-planner/SKILL.md +199 -0
  29. package/.cursor/skills/web-analyzer/SKILL.md +310 -0
  30. package/.cursor/skills/web-crawler/SKILL.md +252 -0
  31. package/AGENTS.md +32 -11
  32. package/CLAUDE.md +78 -33
  33. package/README.md +77 -11
  34. package/designs/DESIGN_CATALOG.md +17 -15
  35. package/designs/DESIGN_PRINCIPLES.md +53 -0
  36. package/designs/brands/accessible-high-contrast.md +14 -0
  37. package/designs/brands/corporate-professional.md +14 -0
  38. package/designs/brands/dark-luxe.md +14 -0
  39. package/designs/brands/kids-playful.md +14 -0
  40. package/designs/brands/medical-clinical.md +14 -0
  41. package/designs/brands/modern-minimal.md +14 -0
  42. package/designs/brands/nature-organic.md +14 -0
  43. package/designs/brands/neo-brutalist.md +14 -0
  44. package/designs/brands/retro-vintage.md +14 -0
  45. package/designs/brands/soft-gradient.md +14 -0
  46. package/designs/brands/sport-athletic.md +14 -0
  47. package/designs/brands/tech-dynamic.md +14 -0
  48. package/designs/brands/vibrant-playful.md +14 -0
  49. package/dist/cli.d.ts +4 -2
  50. package/dist/cli.d.ts.map +1 -1
  51. package/dist/cli.js +123 -11
  52. package/dist/cli.js.map +1 -1
  53. package/dist/config.d.ts +8 -1
  54. package/dist/config.d.ts.map +1 -1
  55. package/dist/config.js +6 -1
  56. package/dist/config.js.map +1 -1
  57. package/dist/engines/claude-engine.d.ts.map +1 -1
  58. package/dist/engines/claude-engine.js +16 -4
  59. package/dist/engines/claude-engine.js.map +1 -1
  60. package/dist/engines/types.d.ts +1 -1
  61. package/dist/engines/types.d.ts.map +1 -1
  62. package/dist/engines/types.js +31 -2
  63. package/dist/engines/types.js.map +1 -1
  64. package/dist/github.d.ts +3 -0
  65. package/dist/github.d.ts.map +1 -1
  66. package/dist/github.js +47 -4
  67. package/dist/github.js.map +1 -1
  68. package/dist/index.js +294 -16
  69. package/dist/index.js.map +1 -1
  70. package/dist/prompt-builder.d.ts +17 -1
  71. package/dist/prompt-builder.d.ts.map +1 -1
  72. package/dist/prompt-builder.js +272 -1
  73. package/dist/prompt-builder.js.map +1 -1
  74. package/dist/validator.d.ts +7 -2
  75. package/dist/validator.d.ts.map +1 -1
  76. package/dist/validator.js +61 -41
  77. package/dist/validator.js.map +1 -1
  78. package/dist/workspace.js +2 -2
  79. package/dist/workspace.js.map +1 -1
  80. package/package.json +2 -4
  81. package/prompts/agent-prompt.md +35 -18
  82. package/prompts/deep-test-agent-prompt.md +122 -0
  83. package/prompts/fix-agent-prompt.md +90 -0
  84. package/prompts/quick-agent-prompt.md +32 -2
  85. package/prompts/scratch-agent-prompt.md +5 -8
  86. package/prompts/web-clone-agent-prompt.md +179 -0
  87. package/templates/android/BookTemplate/app/src/main/kotlin/com/appship/book/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  88. package/templates/android/ChatTemplate/app/src/main/kotlin/com/appship/chat/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  89. package/templates/android/ChatTemplate/app/src/main/kotlin/com/appship/chat/features/conversations/ConversationsScreen.kt +1 -1
  90. package/templates/android/DashTemplate/app/src/main/kotlin/com/appship/dash/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  91. package/templates/android/DashTemplate/app/src/main/kotlin/com/appship/dash/features/navigation/MainScreen.kt +1 -0
  92. package/templates/android/FamilyTemplate/app/src/main/java/com/appship/family/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  93. package/templates/android/FamilyTemplate/app/src/main/java/com/appship/family/features/navigation/MainNavigation.kt +5 -1
  94. package/templates/android/FinanceTemplate/app/src/main/kotlin/com/appship/finance/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  95. package/templates/android/GameTemplate/app/src/main/kotlin/com/appship/game/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  96. package/templates/android/GameTemplate/app/src/main/kotlin/com/appship/game/core/animation/MotionPreferencesScreen.kt +3 -3
  97. package/templates/android/GameTemplate/app/src/main/kotlin/com/appship/game/features/navigation/Navigation.kt +1 -1
  98. package/templates/android/GameTemplate/app/src/main/kotlin/com/appship/game/features/settings/SettingsScreen.kt +1 -1
  99. package/templates/android/HealthTemplate/app/src/main/kotlin/com/appship/health/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  100. package/templates/android/LearnTemplate/app/src/main/kotlin/com/appship/learn/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  101. package/templates/android/MapTemplate/app/src/main/kotlin/com/appship/map/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  102. package/templates/android/MediaTemplate/app/src/main/kotlin/com/appship/media/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  103. package/templates/android/MediaTemplate/app/src/main/kotlin/com/appship/media/features/settings/SettingsScreen.kt +3 -2
  104. package/templates/android/ReferenceTemplate/app/src/main/kotlin/com/appship/reference/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  105. package/templates/android/ReferenceTemplate/app/src/main/kotlin/com/appship/reference/features/settings/SettingsScreen.kt +1 -1
  106. package/templates/android/ShopTemplate/app/src/main/kotlin/com/appship/shop/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  107. package/templates/android/ShopTemplate/app/src/main/kotlin/com/appship/shop/features/cart/CartScreen.kt +3 -2
  108. package/templates/android/Skeleton/app/src/main/kotlin/com/appship/skeleton/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  109. package/templates/android/Skeleton/tests/03_detail_screen.yaml +1 -1
  110. package/templates/android/Skeleton/tests/04_favorites.yaml +1 -1
  111. package/templates/android/Skeleton/tests/08_full_e2e.yaml +7 -1
  112. package/templates/android/SocialTemplate/app/src/main/kotlin/com/appship/social/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  113. package/templates/android/TaskTemplate/app/src/main/kotlin/com/appship/task/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  114. package/templates/android/TaskTemplate/app/src/main/kotlin/com/appship/task/features/settings/SettingsScreen.kt +3 -2
  115. package/templates/android/TrackTemplate/app/src/main/kotlin/com/appship/track/core/animation/AnimatedTransitionsModifiers.kt +188 -0
  116. package/templates/ios/BookTemplate/BookTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  117. package/templates/ios/ChatTemplate/ChatTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  118. package/templates/ios/DashTemplate/DashTemplate/App/AppConfig.swift +1 -0
  119. package/templates/ios/DashTemplate/DashTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  120. package/templates/ios/DashTemplate/DashTemplate/Core/Strings.swift +13 -0
  121. package/templates/ios/DashTemplate/DashTemplate.xcodeproj/project.pbxproj +32 -20
  122. package/templates/ios/FamilyTemplate/FamilyTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  123. package/templates/ios/FinanceTemplate/FinanceTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  124. package/templates/ios/FinanceTemplate/FinanceTemplate/Core/Strings.swift +42 -0
  125. package/templates/ios/FinanceTemplate/FinanceTemplate.xcodeproj/project.pbxproj +36 -30
  126. package/templates/ios/GameTemplate/GameTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  127. package/templates/ios/HealthTemplate/HealthTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  128. package/templates/ios/LearnTemplate/LearnTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  129. package/templates/ios/MapTemplate/MapTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  130. package/templates/ios/MediaTemplate/MediaTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  131. package/templates/ios/ReferenceTemplate/ReferenceTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  132. package/templates/ios/ReferenceTemplate/ReferenceTemplate/Core/Strings.swift +12 -0
  133. package/templates/ios/ReferenceTemplate/ReferenceTemplate/Features/SkeletonLoading/SkeletonLoadingView.swift +2 -37
  134. package/templates/ios/ShopTemplate/ShopTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  135. package/templates/ios/Skeleton/Skeleton/Core/Animation/AnimatedTransitionsView.swift +201 -0
  136. package/templates/ios/Skeleton/tests/08_full_e2e.yaml +4 -0
  137. package/templates/ios/SocialTemplate/SocialTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  138. package/templates/ios/TaskTemplate/TaskTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  139. package/templates/ios/TrackTemplate/TrackTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
  140. package/templates/react-native/BookTemplate/src/animation/useAnimatedList.ts +219 -2
  141. package/templates/react-native/BookTemplate/src/animation/useMotionPreferences.ts +23 -9
  142. package/templates/react-native/BookTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
  143. package/templates/react-native/ChatTemplate/src/animation/useAnimatedList.ts +219 -2
  144. package/templates/react-native/ChatTemplate/src/animation/useMotionPreferences.ts +23 -9
  145. package/templates/react-native/ChatTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
  146. package/templates/react-native/DashTemplate/src/animation/useAnimatedList.ts +219 -2
  147. package/templates/react-native/DashTemplate/src/animation/useMotionPreferences.ts +23 -9
  148. package/templates/react-native/DashTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
  149. package/templates/react-native/FamilyTemplate/src/animation/useAnimatedList.ts +219 -2
  150. package/templates/react-native/FamilyTemplate/src/animation/useMotionPreferences.ts +23 -9
  151. package/templates/react-native/FamilyTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
  152. package/templates/react-native/FinanceTemplate/src/animation/useAnimatedList.ts +219 -2
  153. package/templates/react-native/FinanceTemplate/src/animation/useMotionPreferences.ts +23 -9
  154. package/templates/react-native/FinanceTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
  155. package/templates/react-native/GameTemplate/src/animation/useAnimatedList.ts +219 -2
  156. package/templates/react-native/GameTemplate/src/animation/useMotionPreferences.ts +23 -9
  157. package/templates/react-native/GameTemplate/src/screens/GameDetail/GameDetailScreen.tsx +2 -1
  158. package/templates/react-native/GameTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
  159. package/templates/react-native/HealthTemplate/src/animation/useAnimatedList.ts +219 -2
  160. package/templates/react-native/HealthTemplate/src/animation/useMotionPreferences.ts +23 -9
  161. package/templates/react-native/HealthTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
  162. package/templates/react-native/HealthTemplate/src/screens/WorkoutDetail/WorkoutDetailScreen.tsx +1 -1
  163. package/templates/react-native/LearnTemplate/src/animation/useAnimatedList.ts +219 -2
  164. package/templates/react-native/LearnTemplate/src/animation/useMotionPreferences.ts +23 -9
  165. package/templates/react-native/LearnTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
  166. package/templates/react-native/MapTemplate/src/animation/useAnimatedList.ts +219 -2
  167. package/templates/react-native/MapTemplate/src/animation/useMotionPreferences.ts +23 -9
  168. package/templates/react-native/MapTemplate/src/screens/Map/MapScreen.tsx +14 -0
  169. package/templates/react-native/MapTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
  170. package/templates/react-native/MediaTemplate/src/animation/useAnimatedList.ts +219 -2
  171. package/templates/react-native/MediaTemplate/src/animation/useMotionPreferences.ts +23 -9
  172. package/templates/react-native/MediaTemplate/src/screens/PlaylistDetail/PlaylistDetailScreen.tsx +1 -1
  173. package/templates/react-native/MediaTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
  174. package/templates/react-native/ReferenceTemplate/src/animation/useAnimatedList.ts +219 -2
  175. package/templates/react-native/ReferenceTemplate/src/animation/useMotionPreferences.ts +23 -9
  176. package/templates/react-native/ReferenceTemplate/src/screens/Settings/SettingsScreen.tsx +1 -1
  177. package/templates/react-native/ShopTemplate/src/animation/useAnimatedList.ts +219 -2
  178. package/templates/react-native/ShopTemplate/src/animation/useMotionPreferences.ts +23 -9
  179. package/templates/react-native/ShopTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
  180. package/templates/react-native/Skeleton/TESTING_MANIFEST.md +1 -1
  181. package/templates/react-native/Skeleton/src/animation/useAnimatedList.ts +219 -2
  182. package/templates/react-native/Skeleton/src/animation/useMotionPreferences.ts +23 -9
  183. package/templates/react-native/Skeleton/src/screens/Profile/ProfileScreen.tsx +1 -1
  184. package/templates/react-native/Skeleton/tests/07_profile.yaml +3 -2
  185. package/templates/react-native/Skeleton/tests/08_full_e2e.yaml +12 -1
  186. package/templates/react-native/SocialTemplate/src/animation/useAnimatedList.ts +219 -2
  187. package/templates/react-native/SocialTemplate/src/animation/useMotionPreferences.ts +23 -9
  188. package/templates/react-native/SocialTemplate/src/screens/Feed/FeedScreen.tsx +1 -0
  189. package/templates/react-native/SocialTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
  190. package/templates/react-native/TaskTemplate/src/animation/useAnimatedList.ts +219 -2
  191. package/templates/react-native/TaskTemplate/src/animation/useMotionPreferences.ts +23 -9
  192. package/templates/react-native/TaskTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
  193. package/templates/react-native/TrackTemplate/src/animation/useAnimatedList.ts +219 -2
  194. package/templates/react-native/TrackTemplate/src/animation/useMotionPreferences.ts +23 -9
  195. package/templates/react-native/TrackTemplate/src/screens/Settings/SettingsScreen.tsx +1 -1
  196. package/templates/shared/ios/AnimatedTransitions/AnimatedTransitionsView.swift +233 -93
  197. package/.claude/agents/template-selector.md +0 -39
  198. package/.claude/skills/module-selector/SKILL.md +0 -81
  199. package/.claude/skills/template-selector/SKILL.md +0 -44
  200. package/.cursor/agents/template-selector.md +0 -52
  201. package/.cursor/skills/module-selector/SKILL.md +0 -135
  202. package/.cursor/skills/template-selector/SKILL.md +0 -123
@@ -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
@@ -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
  }
@@ -24,4 +24,17 @@ enum Strings {
24
24
  static let noConnectionSubtitle = "Please check your connection and try again"
25
25
  static let retryButton = "Retry"
26
26
  }
27
+
28
+ // MARK: - Motion Preferences
29
+ enum MotionPreferences {
30
+ static let animationLevel = "Animations"
31
+ static let description = "Controls how animations and transitions appear throughout the app. System Reduce Motion setting is always respected."
32
+ static let reduceMotionActive = "System Reduce Motion is enabled. Animations will be simplified."
33
+ static let intensityFull = "Full"
34
+ static let intensityReduced = "Reduced"
35
+ static let intensityOff = "Off"
36
+ static let fullDescription = "All animations and transitions enabled"
37
+ static let reducedDescription = "Simplified cross-fade transitions only"
38
+ static let offDescription = "No animations — instant state changes"
39
+ }
27
40
  }
@@ -9,6 +9,9 @@
9
9
  /* Begin PBXBuildFile section */
10
10
  0BE4C91CEB93C75BB74C94CC /* MainTabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1F52320326E6232324479D1 /* MainTabView.swift */; };
11
11
  1A8D3BD8F154C8D93F0AE9EF /* WidgetEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF385475DFD0F03D69FED48A /* WidgetEntity.swift */; };
12
+ 21FDAF2F31552330D95A6BAA /* MotionPreferencesService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47CD09F9FB8E4CEAE829A262 /* MotionPreferencesService.swift */; };
13
+ 2AF001D43064F72A2F795AB0 /* AnimatedTransitionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C63249BCD207454805D7E90 /* AnimatedTransitionsView.swift */; };
14
+ 355C85E6841AD044C365AD66 /* MotionPreferencesModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7D5D277B1CA54D4CAAE163B /* MotionPreferencesModels.swift */; };
12
15
  3D5C5D854CEF24118760720F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BF16697D32215D781AF9B14D /* Assets.xcassets */; };
13
16
  3E52343351087A1FF413D681 /* ReportsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830768B7341CC7E255A63C6E /* ReportsView.swift */; };
14
17
  41D20BD9E570D8DD475DF076 /* WidgetCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF7A196EF15A877A206FF8D /* WidgetCardView.swift */; };
@@ -24,7 +27,10 @@
24
27
  728D5700267E364A538DEC96 /* DataSourceResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74A1BDEC308404BEC6876EF7 /* DataSourceResolver.swift */; };
25
28
  79748FDC3363CC72F03D62CF /* DashboardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E824DE55DE9E8AF070AC3F9E /* DashboardView.swift */; };
26
29
  82AEFE82B982FB3328DD6208 /* AlertsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87AA58D9FFA46B6E77E5EF0E /* AlertsView.swift */; };
30
+ 85D34F0FECC1F68732D0D045 /* AnimatedTransitionsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1078CA48BA4AD991E86EC04 /* AnimatedTransitionsService.swift */; };
27
31
  8D6159AB254E7FC6F8EF9BB5 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B0DBBE1900E60F62A37281B /* SettingsView.swift */; };
32
+ 960E6CFA6ADE11E0C6E22DEE /* AnimatedTransitionsModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF7E1F70AC77885E742E2493 /* AnimatedTransitionsModels.swift */; };
33
+ 966A70D62A1A724EE7DB7D2D /* MotionPreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C9FC2D8415D5F2804A828B /* MotionPreferencesView.swift */; };
28
34
  9FE3D67B3677A864F7FCFD5C /* ModelsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BACEF3A8CFE0DF2F946411E2 /* ModelsTests.swift */; };
29
35
  B728B8ED78312CA38506F409 /* DataSourceEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0D6890A652CA124C43A08AA /* DataSourceEntity.swift */; };
30
36
  BCBA5211DE364070923D4766 /* DashTemplateApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = B23047E6E4A0BD14D30AC1B3 /* DashTemplateApp.swift */; };
@@ -56,8 +62,10 @@
56
62
  0B0DBBE1900E60F62A37281B /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
57
63
  17E81ED8FD5321DA82068915 /* MockDataProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockDataProvider.swift; sourceTree = "<group>"; };
58
64
  1A810A28190B53469CB429C8 /* EmptyStateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyStateView.swift; sourceTree = "<group>"; };
65
+ 1C63249BCD207454805D7E90 /* AnimatedTransitionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnimatedTransitionsView.swift; sourceTree = "<group>"; };
59
66
  1C76F22D4CC18C16AE5698C5 /* DataPointEntity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataPointEntity.swift; sourceTree = "<group>"; };
60
67
  1E37CEF816F47AAA6618E699 /* CoreDataStack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataStack.swift; sourceTree = "<group>"; };
68
+ 47CD09F9FB8E4CEAE829A262 /* MotionPreferencesService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MotionPreferencesService.swift; sourceTree = "<group>"; };
61
69
  4CF7A196EF15A877A206FF8D /* WidgetCardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetCardView.swift; sourceTree = "<group>"; };
62
70
  639611E2568702F91BB4EC3D /* Colors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Colors.swift; sourceTree = "<group>"; };
63
71
  67C3ED3656E108EEF91DFE6B /* DataSourcesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataSourcesView.swift; sourceTree = "<group>"; };
@@ -74,13 +82,17 @@
74
82
  BF16697D32215D781AF9B14D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
75
83
  BF385475DFD0F03D69FED48A /* WidgetEntity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetEntity.swift; sourceTree = "<group>"; };
76
84
  CD5DDD0D3C4EBE8550325EF4 /* LocalStorageProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalStorageProvider.swift; sourceTree = "<group>"; };
85
+ D1078CA48BA4AD991E86EC04 /* AnimatedTransitionsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnimatedTransitionsService.swift; sourceTree = "<group>"; };
77
86
  D8581658679C25D5DE95D8AD /* ChartDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChartDetailView.swift; sourceTree = "<group>"; };
78
87
  E0D6890A652CA124C43A08AA /* DataSourceEntity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataSourceEntity.swift; sourceTree = "<group>"; };
79
88
  E1F52320326E6232324479D1 /* MainTabView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTabView.swift; sourceTree = "<group>"; };
89
+ E4C9FC2D8415D5F2804A828B /* MotionPreferencesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MotionPreferencesView.swift; sourceTree = "<group>"; };
80
90
  E74AEF186268B5ECCBF001E5 /* MetricEntity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetricEntity.swift; sourceTree = "<group>"; };
81
91
  E824DE55DE9E8AF070AC3F9E /* DashboardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DashboardView.swift; sourceTree = "<group>"; };
82
92
  F36C08012FCFC44F8C559145 /* AlertEntity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertEntity.swift; sourceTree = "<group>"; };
93
+ F7D5D277B1CA54D4CAAE163B /* MotionPreferencesModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MotionPreferencesModels.swift; sourceTree = "<group>"; };
83
94
  F9E99A12D50E8DBF4CC83A58 /* AppConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppConfig.swift; sourceTree = "<group>"; };
95
+ FF7E1F70AC77885E742E2493 /* AnimatedTransitionsModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnimatedTransitionsModels.swift; sourceTree = "<group>"; };
84
96
  /* End PBXFileReference section */
85
97
 
86
98
  /* Begin PBXGroup section */
@@ -165,6 +177,19 @@
165
177
  path = Data;
166
178
  sourceTree = "<group>";
167
179
  };
180
+ 5DF211C08F5986037B0ACBFC /* Animation */ = {
181
+ isa = PBXGroup;
182
+ children = (
183
+ FF7E1F70AC77885E742E2493 /* AnimatedTransitionsModels.swift */,
184
+ D1078CA48BA4AD991E86EC04 /* AnimatedTransitionsService.swift */,
185
+ 1C63249BCD207454805D7E90 /* AnimatedTransitionsView.swift */,
186
+ F7D5D277B1CA54D4CAAE163B /* MotionPreferencesModels.swift */,
187
+ 47CD09F9FB8E4CEAE829A262 /* MotionPreferencesService.swift */,
188
+ E4C9FC2D8415D5F2804A828B /* MotionPreferencesView.swift */,
189
+ );
190
+ path = Animation;
191
+ sourceTree = "<group>";
192
+ };
168
193
  6C207A201492FD4D6B296D7D /* Resources */ = {
169
194
  isa = PBXGroup;
170
195
  children = (
@@ -284,26 +309,13 @@
284
309
  children = (
285
310
  1A810A28190B53469CB429C8 /* EmptyStateView.swift */,
286
311
  801943F78745D3BC357E1D0E /* Strings.swift */,
287
- ADB79762DCD54BCCAF1F5DF1 /* Animation */,
312
+ 5DF211C08F5986037B0ACBFC /* Animation */,
288
313
  73BE6B187F51A045397192F8 /* Components */,
289
314
  1D63C5BF239B420B41FF307D /* Theme */,
290
315
  );
291
316
  path = Core;
292
317
  sourceTree = "<group>";
293
318
  };
294
- ADB79762DCD54BCCAF1F5DF1 /* Animation */ = {
295
- isa = PBXGroup;
296
- children = (
297
- 12D07B13660E4E92A273B5F3 /* AnimatedTransitionsModels.swift */,
298
- 13E50F8041294D5886C226C5 /* AnimatedTransitionsService.swift */,
299
- 3FEB10E0B44440259FF6DA81 /* AnimatedTransitionsView.swift */,
300
- 5B752E9A54EA4207B0C275A6 /* MotionPreferencesModels.swift */,
301
- 0932E20F8A25466687E9DBBF /* MotionPreferencesService.swift */,
302
- 568C3B72B6134071BCA74B8E /* MotionPreferencesView.swift */,
303
- );
304
- path = Animation;
305
- sourceTree = "<group>";
306
- };
307
319
  /* End PBXGroup section */
308
320
 
309
321
  /* Begin PBXNativeTarget section */
@@ -390,6 +402,9 @@
390
402
  files = (
391
403
  C7A64B80C3AD62BA15105B46 /* AlertEntity.swift in Sources */,
392
404
  82AEFE82B982FB3328DD6208 /* AlertsView.swift in Sources */,
405
+ 960E6CFA6ADE11E0C6E22DEE /* AnimatedTransitionsModels.swift in Sources */,
406
+ 85D34F0FECC1F68732D0D045 /* AnimatedTransitionsService.swift in Sources */,
407
+ 2AF001D43064F72A2F795AB0 /* AnimatedTransitionsView.swift in Sources */,
393
408
  BF7769F85C62ECF6C3C59FEA /* AppConfig.swift in Sources */,
394
409
  671F910DD6E82465B7992E2C /* ChartDetailView.swift in Sources */,
395
410
  5CD7770D5AF5B16299DC18ED /* Colors.swift in Sources */,
@@ -408,18 +423,15 @@
408
423
  E003E0CF340061E20E8AD903 /* MetricEntity.swift in Sources */,
409
424
  429EDAAFE9DD8D1CC7058C4E /* MockDataProvider.swift in Sources */,
410
425
  7198460EE53D1686B9662B03 /* Models.swift in Sources */,
426
+ 355C85E6841AD044C365AD66 /* MotionPreferencesModels.swift in Sources */,
427
+ 21FDAF2F31552330D95A6BAA /* MotionPreferencesService.swift in Sources */,
428
+ 966A70D62A1A724EE7DB7D2D /* MotionPreferencesView.swift in Sources */,
411
429
  62895A0C2BCB12B46406A76A /* ReportEntity.swift in Sources */,
412
430
  3E52343351087A1FF413D681 /* ReportsView.swift in Sources */,
413
431
  8D6159AB254E7FC6F8EF9BB5 /* SettingsView.swift in Sources */,
414
432
  EC9A5B3DD22BD0A6D4B0A471 /* Strings.swift in Sources */,
415
433
  41D20BD9E570D8DD475DF076 /* WidgetCardView.swift in Sources */,
416
434
  1A8D3BD8F154C8D93F0AE9EF /* WidgetEntity.swift in Sources */,
417
- 5AF808372FF5445CA4403966 /* AnimatedTransitionsModels.swift in Sources */,
418
- 63973BA002FF4C47A99CE568 /* AnimatedTransitionsService.swift in Sources */,
419
- FF22F365178049A9A57D7067 /* AnimatedTransitionsView.swift in Sources */,
420
- 984CC68FFE3C4033ABBBA570 /* MotionPreferencesModels.swift in Sources */,
421
- D5AF641E444D438CB32255AF /* MotionPreferencesService.swift in Sources */,
422
- ABF06AAC0EA64B1A8FB13962 /* MotionPreferencesView.swift in Sources */,
423
435
  );
424
436
  runOnlyForDeploymentPostprocessing = 0;
425
437
  };