create-stackr 0.2.0

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 (274) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +642 -0
  3. package/bin/cli.js +12 -0
  4. package/dist/cli.d.ts +3 -0
  5. package/dist/cli.d.ts.map +1 -0
  6. package/dist/cli.js +113 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/config/dependencies.d.ts +82 -0
  9. package/dist/config/dependencies.d.ts.map +1 -0
  10. package/dist/config/dependencies.js +82 -0
  11. package/dist/config/dependencies.js.map +1 -0
  12. package/dist/config/presets.d.ts +3 -0
  13. package/dist/config/presets.d.ts.map +1 -0
  14. package/dist/config/presets.js +174 -0
  15. package/dist/config/presets.js.map +1 -0
  16. package/dist/generators/index.d.ts +40 -0
  17. package/dist/generators/index.d.ts.map +1 -0
  18. package/dist/generators/index.js +130 -0
  19. package/dist/generators/index.js.map +1 -0
  20. package/dist/generators/onboarding.d.ts +8 -0
  21. package/dist/generators/onboarding.d.ts.map +1 -0
  22. package/dist/generators/onboarding.js +141 -0
  23. package/dist/generators/onboarding.js.map +1 -0
  24. package/dist/index.d.ts +3 -0
  25. package/dist/index.d.ts.map +1 -0
  26. package/dist/index.js +65 -0
  27. package/dist/index.js.map +1 -0
  28. package/dist/prompts/features.d.ts +14 -0
  29. package/dist/prompts/features.d.ts.map +1 -0
  30. package/dist/prompts/features.js +96 -0
  31. package/dist/prompts/features.js.map +1 -0
  32. package/dist/prompts/index.d.ts +3 -0
  33. package/dist/prompts/index.d.ts.map +1 -0
  34. package/dist/prompts/index.js +93 -0
  35. package/dist/prompts/index.js.map +1 -0
  36. package/dist/prompts/onboarding.d.ts +6 -0
  37. package/dist/prompts/onboarding.d.ts.map +1 -0
  38. package/dist/prompts/onboarding.js +37 -0
  39. package/dist/prompts/onboarding.js.map +1 -0
  40. package/dist/prompts/orm.d.ts +3 -0
  41. package/dist/prompts/orm.d.ts.map +1 -0
  42. package/dist/prompts/orm.js +23 -0
  43. package/dist/prompts/orm.js.map +1 -0
  44. package/dist/prompts/packageManager.d.ts +2 -0
  45. package/dist/prompts/packageManager.d.ts.map +1 -0
  46. package/dist/prompts/packageManager.js +18 -0
  47. package/dist/prompts/packageManager.js.map +1 -0
  48. package/dist/prompts/platform.d.ts +3 -0
  49. package/dist/prompts/platform.d.ts.map +1 -0
  50. package/dist/prompts/platform.js +21 -0
  51. package/dist/prompts/platform.js.map +1 -0
  52. package/dist/prompts/preset.d.ts +4 -0
  53. package/dist/prompts/preset.d.ts.map +1 -0
  54. package/dist/prompts/preset.js +165 -0
  55. package/dist/prompts/preset.js.map +1 -0
  56. package/dist/prompts/project.d.ts +2 -0
  57. package/dist/prompts/project.d.ts.map +1 -0
  58. package/dist/prompts/project.js +27 -0
  59. package/dist/prompts/project.js.map +1 -0
  60. package/dist/prompts/sdks.d.ts +2 -0
  61. package/dist/prompts/sdks.d.ts.map +1 -0
  62. package/dist/prompts/sdks.js +46 -0
  63. package/dist/prompts/sdks.js.map +1 -0
  64. package/dist/types/index.d.ts +77 -0
  65. package/dist/types/index.d.ts.map +1 -0
  66. package/dist/types/index.js +25 -0
  67. package/dist/types/index.js.map +1 -0
  68. package/dist/utils/cleanup.d.ts +5 -0
  69. package/dist/utils/cleanup.d.ts.map +1 -0
  70. package/dist/utils/cleanup.js +38 -0
  71. package/dist/utils/cleanup.js.map +1 -0
  72. package/dist/utils/copy.d.ts +10 -0
  73. package/dist/utils/copy.d.ts.map +1 -0
  74. package/dist/utils/copy.js +53 -0
  75. package/dist/utils/copy.js.map +1 -0
  76. package/dist/utils/errors.d.ts +33 -0
  77. package/dist/utils/errors.d.ts.map +1 -0
  78. package/dist/utils/errors.js +136 -0
  79. package/dist/utils/errors.js.map +1 -0
  80. package/dist/utils/git.d.ts +5 -0
  81. package/dist/utils/git.d.ts.map +1 -0
  82. package/dist/utils/git.js +33 -0
  83. package/dist/utils/git.js.map +1 -0
  84. package/dist/utils/logger.d.ts +9 -0
  85. package/dist/utils/logger.d.ts.map +1 -0
  86. package/dist/utils/logger.js +22 -0
  87. package/dist/utils/logger.js.map +1 -0
  88. package/dist/utils/package.d.ts +16 -0
  89. package/dist/utils/package.d.ts.map +1 -0
  90. package/dist/utils/package.js +86 -0
  91. package/dist/utils/package.js.map +1 -0
  92. package/dist/utils/system-validation.d.ts +9 -0
  93. package/dist/utils/system-validation.d.ts.map +1 -0
  94. package/dist/utils/system-validation.js +31 -0
  95. package/dist/utils/system-validation.js.map +1 -0
  96. package/dist/utils/template.d.ts +20 -0
  97. package/dist/utils/template.d.ts.map +1 -0
  98. package/dist/utils/template.js +234 -0
  99. package/dist/utils/template.js.map +1 -0
  100. package/dist/utils/validation.d.ts +8 -0
  101. package/dist/utils/validation.d.ts.map +1 -0
  102. package/dist/utils/validation.js +94 -0
  103. package/dist/utils/validation.js.map +1 -0
  104. package/package.json +96 -0
  105. package/templates/base/backend/.dockerignore.ejs +62 -0
  106. package/templates/base/backend/.env.example.ejs +116 -0
  107. package/templates/base/backend/Dockerfile.ejs +142 -0
  108. package/templates/base/backend/controllers/event-queue/index.ts +20 -0
  109. package/templates/base/backend/controllers/event-queue/workers/user.ts +39 -0
  110. package/templates/base/backend/controllers/rest-api/index.ts +48 -0
  111. package/templates/base/backend/controllers/rest-api/plugins/auth.ts +152 -0
  112. package/templates/base/backend/controllers/rest-api/plugins/config.ts +64 -0
  113. package/templates/base/backend/controllers/rest-api/plugins/error-handler.ts +118 -0
  114. package/templates/base/backend/controllers/rest-api/routes/auth.ts.ejs +180 -0
  115. package/templates/base/backend/controllers/rest-api/routes/device-sessions.ts +197 -0
  116. package/templates/base/backend/controllers/rest-api/routes/oauth-web.ts.ejs +375 -0
  117. package/templates/base/backend/controllers/rest-api/server.ts.ejs +87 -0
  118. package/templates/base/backend/domain/device-session/repository.drizzle.ts +209 -0
  119. package/templates/base/backend/domain/device-session/repository.prisma.ts +248 -0
  120. package/templates/base/backend/domain/device-session/schema.ts +72 -0
  121. package/templates/base/backend/domain/session/repository.drizzle.ts +72 -0
  122. package/templates/base/backend/domain/session/repository.prisma.ts +72 -0
  123. package/templates/base/backend/domain/session/schema.ts +29 -0
  124. package/templates/base/backend/domain/user/repository.drizzle.ts +127 -0
  125. package/templates/base/backend/domain/user/repository.prisma.ts +115 -0
  126. package/templates/base/backend/domain/user/schema.ts +14 -0
  127. package/templates/base/backend/drizzle/schema.drizzle.ts +111 -0
  128. package/templates/base/backend/drizzle.config.drizzle.ts +13 -0
  129. package/templates/base/backend/lib/auth.drizzle.ts.ejs +104 -0
  130. package/templates/base/backend/lib/auth.prisma.ts.ejs +97 -0
  131. package/templates/base/backend/lib/constants.ts.ejs +29 -0
  132. package/templates/base/backend/package.json.ejs +50 -0
  133. package/templates/base/backend/prisma/schema.prisma.ejs +102 -0
  134. package/templates/base/backend/prisma.config.prisma.ts +12 -0
  135. package/templates/base/backend/tsconfig.json +39 -0
  136. package/templates/base/backend/utils/db.drizzle.ts +41 -0
  137. package/templates/base/backend/utils/db.prisma.ts +51 -0
  138. package/templates/base/backend/utils/email.ts.ejs +35 -0
  139. package/templates/base/backend/utils/errors.ts +348 -0
  140. package/templates/base/backend/utils/redis.ts.ejs +279 -0
  141. package/templates/base/mobile/.env.example.ejs +35 -0
  142. package/templates/base/mobile/.gitignore.ejs +167 -0
  143. package/templates/base/mobile/app/+not-found.tsx +85 -0
  144. package/templates/base/mobile/app/_layout.tsx.ejs +71 -0
  145. package/templates/base/mobile/app.json.ejs +88 -0
  146. package/templates/base/mobile/assets/images/adaptive-icon.png +0 -0
  147. package/templates/base/mobile/assets/images/favicon.png +0 -0
  148. package/templates/base/mobile/assets/images/icon.png +0 -0
  149. package/templates/base/mobile/assets/images/onboarding_page_1.png +0 -0
  150. package/templates/base/mobile/assets/images/onboarding_page_2.png +0 -0
  151. package/templates/base/mobile/assets/images/onboarding_page_3.png +0 -0
  152. package/templates/base/mobile/assets/images/paywall_image.png +0 -0
  153. package/templates/base/mobile/assets/images/splash.png +0 -0
  154. package/templates/base/mobile/eas.json.ejs +49 -0
  155. package/templates/base/mobile/metro.config.js +9 -0
  156. package/templates/base/mobile/package.json.ejs +53 -0
  157. package/templates/base/mobile/src/components/ui/Button.tsx +131 -0
  158. package/templates/base/mobile/src/components/ui/Card.tsx +68 -0
  159. package/templates/base/mobile/src/components/ui/IconSymbol.tsx +90 -0
  160. package/templates/base/mobile/src/components/ui/Input.tsx +142 -0
  161. package/templates/base/mobile/src/components/ui/LoadingSpinner.tsx +98 -0
  162. package/templates/base/mobile/src/components/ui/OnboardingLayout.tsx +356 -0
  163. package/templates/base/mobile/src/components/ui/PaywallLayout.tsx +311 -0
  164. package/templates/base/mobile/src/components/ui/Skeleton.tsx +58 -0
  165. package/templates/base/mobile/src/components/ui/index.ts +6 -0
  166. package/templates/base/mobile/src/constants/Theme.ts +163 -0
  167. package/templates/base/mobile/src/context/ThemeContext.tsx +157 -0
  168. package/templates/base/mobile/src/lib/auth-client.ts.ejs +51 -0
  169. package/templates/base/mobile/src/services/api.ts.ejs +71 -0
  170. package/templates/base/mobile/src/services/errorService.ts +179 -0
  171. package/templates/base/mobile/src/services/sdkInitializer.ts.ejs +36 -0
  172. package/templates/base/mobile/src/store/index.ts.ejs +18 -0
  173. package/templates/base/mobile/src/store/ui.store.ts +100 -0
  174. package/templates/base/mobile/src/utils/formatters.ts +105 -0
  175. package/templates/base/mobile/src/utils/logger.ts +73 -0
  176. package/templates/base/mobile/src/utils/responsive.ts +234 -0
  177. package/templates/base/mobile/tsconfig.json +32 -0
  178. package/templates/base/web/.env.example.ejs +26 -0
  179. package/templates/base/web/components.json +22 -0
  180. package/templates/base/web/eslint.config.mjs +18 -0
  181. package/templates/base/web/next.config.ts +7 -0
  182. package/templates/base/web/package.json.ejs +35 -0
  183. package/templates/base/web/postcss.config.mjs +7 -0
  184. package/templates/base/web/public/.gitkeep +0 -0
  185. package/templates/base/web/public/file.svg +1 -0
  186. package/templates/base/web/public/globe.svg +1 -0
  187. package/templates/base/web/public/next.svg +1 -0
  188. package/templates/base/web/public/vercel.svg +1 -0
  189. package/templates/base/web/public/window.svg +1 -0
  190. package/templates/base/web/src/app/favicon.ico +0 -0
  191. package/templates/base/web/src/app/globals.css +152 -0
  192. package/templates/base/web/src/app/layout.tsx.ejs +54 -0
  193. package/templates/base/web/src/app/page.tsx.ejs +92 -0
  194. package/templates/base/web/src/components/auth/auth-hydrator.tsx.ejs +19 -0
  195. package/templates/base/web/src/components/auth/protected-route.tsx.ejs +109 -0
  196. package/templates/base/web/src/components/providers/device-session-setup.tsx.ejs +56 -0
  197. package/templates/base/web/src/components/providers/theme-provider.tsx +17 -0
  198. package/templates/base/web/src/components/theme-toggle.tsx +34 -0
  199. package/templates/base/web/src/components/ui/button.tsx +62 -0
  200. package/templates/base/web/src/components/ui/card.tsx +92 -0
  201. package/templates/base/web/src/components/ui/input.tsx +21 -0
  202. package/templates/base/web/src/components/ui/label.tsx +24 -0
  203. package/templates/base/web/src/components/ui/skeleton.tsx +13 -0
  204. package/templates/base/web/src/components/ui/spinner.tsx +20 -0
  205. package/templates/base/web/src/hooks/use-device-session.ts.ejs +40 -0
  206. package/templates/base/web/src/hooks/use-session.ts.ejs +56 -0
  207. package/templates/base/web/src/lib/auth/actions.ts.ejs +334 -0
  208. package/templates/base/web/src/lib/auth/config.ts.ejs +65 -0
  209. package/templates/base/web/src/lib/auth/cookies.ts.ejs +74 -0
  210. package/templates/base/web/src/lib/auth/index.ts.ejs +40 -0
  211. package/templates/base/web/src/lib/auth/oauth.ts.ejs +72 -0
  212. package/templates/base/web/src/lib/auth/pkce.ts.ejs +48 -0
  213. package/templates/base/web/src/lib/auth/sessions.ts.ejs +135 -0
  214. package/templates/base/web/src/lib/auth/user-agent.ts.ejs +47 -0
  215. package/templates/base/web/src/lib/device/actions.ts.ejs +148 -0
  216. package/templates/base/web/src/lib/device/id.ts.ejs +74 -0
  217. package/templates/base/web/src/lib/utils.ts +6 -0
  218. package/templates/base/web/src/proxy.ts.ejs +66 -0
  219. package/templates/base/web/src/store/auth.store.ts.ejs +89 -0
  220. package/templates/base/web/src/store/deviceSession.store.ts.ejs +141 -0
  221. package/templates/base/web/tsconfig.json +34 -0
  222. package/templates/features/mobile/auth/app/(auth)/_layout.tsx +16 -0
  223. package/templates/features/mobile/auth/app/(auth)/login.tsx +86 -0
  224. package/templates/features/mobile/auth/app/(auth)/register.tsx +86 -0
  225. package/templates/features/mobile/auth/components/auth/LoginForm.tsx.ejs +349 -0
  226. package/templates/features/mobile/auth/components/auth/RegisterForm.tsx.ejs +407 -0
  227. package/templates/features/mobile/auth/components/auth/index.ts +2 -0
  228. package/templates/features/mobile/auth/hooks/index.ts.ejs +1 -0
  229. package/templates/features/mobile/auth/hooks/useAuth.ts.ejs +367 -0
  230. package/templates/features/mobile/auth/services/deviceSession.ts +370 -0
  231. package/templates/features/mobile/auth/store/deviceSession.store.ts +326 -0
  232. package/templates/features/mobile/onboarding/app/(onboarding)/_layout.tsx.ejs +11 -0
  233. package/templates/features/mobile/onboarding/app/(onboarding)/page-1.tsx.ejs +52 -0
  234. package/templates/features/mobile/onboarding/app/(onboarding)/page-2.tsx.ejs +52 -0
  235. package/templates/features/mobile/onboarding/app/(onboarding)/page-3.tsx.ejs +60 -0
  236. package/templates/features/mobile/paywall/app/paywall.tsx +550 -0
  237. package/templates/features/mobile/tabs/app/(tabs)/_layout.tsx +26 -0
  238. package/templates/features/mobile/tabs/app/(tabs)/index.tsx +565 -0
  239. package/templates/features/web/.gitkeep +0 -0
  240. package/templates/features/web/auth/app/(app)/dashboard/dashboard-client.tsx.ejs +166 -0
  241. package/templates/features/web/auth/app/(app)/dashboard/page.tsx.ejs +24 -0
  242. package/templates/features/web/auth/app/(app)/layout.tsx.ejs +43 -0
  243. package/templates/features/web/auth/app/(app)/settings/sessions/page.tsx.ejs +29 -0
  244. package/templates/features/web/auth/app/(app)/settings/sessions/sessions-client.tsx.ejs +77 -0
  245. package/templates/features/web/auth/app/(auth)/forgot-password/page.tsx.ejs +127 -0
  246. package/templates/features/web/auth/app/(auth)/layout.tsx.ejs +32 -0
  247. package/templates/features/web/auth/app/(auth)/login/page.tsx.ejs +35 -0
  248. package/templates/features/web/auth/app/(auth)/register/page.tsx.ejs +19 -0
  249. package/templates/features/web/auth/app/(auth)/reset-password/page.tsx.ejs +40 -0
  250. package/templates/features/web/auth/app/(auth)/verify-email/page.tsx.ejs +198 -0
  251. package/templates/features/web/auth/app/auth/callback/route.ts.ejs +152 -0
  252. package/templates/features/web/auth/components/auth/login-form.tsx.ejs +100 -0
  253. package/templates/features/web/auth/components/auth/oauth-buttons.tsx.ejs +126 -0
  254. package/templates/features/web/auth/components/auth/password-reset-form.tsx.ejs +103 -0
  255. package/templates/features/web/auth/components/auth/register-form.tsx.ejs +139 -0
  256. package/templates/features/web/auth/components/settings/session-card.tsx.ejs +132 -0
  257. package/templates/integrations/mobile/adjust/services/adjustService.ts.ejs +163 -0
  258. package/templates/integrations/mobile/adjust/store/adjust.store.ts +243 -0
  259. package/templates/integrations/mobile/att/services/attService.ts +84 -0
  260. package/templates/integrations/mobile/att/services/trackingPermissions.ts +208 -0
  261. package/templates/integrations/mobile/att/store/att.store.ts +162 -0
  262. package/templates/integrations/mobile/revenuecat/services/revenuecatService.ts.ejs +174 -0
  263. package/templates/integrations/mobile/revenuecat/store/revenuecat.store.ts +286 -0
  264. package/templates/integrations/mobile/scate/services/scateService.ts.ejs +85 -0
  265. package/templates/integrations/mobile/scate/store/scate.store.ts +125 -0
  266. package/templates/integrations/web/.gitkeep +0 -0
  267. package/templates/shared/.env.example.ejs +21 -0
  268. package/templates/shared/.gitignore.ejs +145 -0
  269. package/templates/shared/README.md.ejs +134 -0
  270. package/templates/shared/docker-compose.prod.yml.ejs +120 -0
  271. package/templates/shared/docker-compose.yml.ejs +129 -0
  272. package/templates/shared/scripts/docker-dev.sh.ejs +395 -0
  273. package/templates/shared/scripts/docker-prod.sh.ejs +542 -0
  274. package/templates/shared/scripts/setup.sh.ejs +979 -0
@@ -0,0 +1,234 @@
1
+ import { Dimensions, Platform } from 'react-native';
2
+
3
+ const { width: screenWidth, height: screenHeight } = Dimensions.get('window');
4
+
5
+ // Base dimensions for scaling (iPhone 14 as reference)
6
+ const baseWidth = 390;
7
+ const baseHeight = 844;
8
+
9
+ // Device type categories based on height
10
+ export const DeviceTypes = {
11
+ SMALL: 'small', // iPhone SE, mini devices (< 700)
12
+ MEDIUM: 'medium', // Standard iPhones (700-900)
13
+ LARGE: 'large', // Plus/Pro Max, tablets (> 900)
14
+ } as const;
15
+
16
+ export type DeviceType = typeof DeviceTypes[keyof typeof DeviceTypes];
17
+
18
+ interface ResponsiveSpacing {
19
+ xs: number;
20
+ sm: number;
21
+ md: number;
22
+ lg: number;
23
+ xl: number;
24
+ xxl: number;
25
+ }
26
+
27
+ interface ResponsiveInterface {
28
+ getDeviceType(): DeviceType;
29
+ scale(size: number): number;
30
+ verticalScale(size: number): number;
31
+ moderateScale(size: number, factor?: number): number;
32
+ wp(percentage: number): number;
33
+ hp(percentage: number): number;
34
+ fontSize(size: number): number;
35
+ getSpacing(size: number): number;
36
+ getSafeHeight(): number;
37
+ getContentHeight(): number;
38
+ getImageContainerSize(): { width: number; height: number };
39
+ getButtonBottomPosition(): number;
40
+ getSectionHeights(): { topSection: number; bottomPadding: number; pageIndicatorMargin: number };
41
+ getPlanCardHeight(): number;
42
+ spacing?: ResponsiveSpacing;
43
+ }
44
+
45
+ export const responsive: ResponsiveInterface = {
46
+ // Get current device type
47
+ getDeviceType(): DeviceType {
48
+ if (screenHeight < 700) return DeviceTypes.SMALL;
49
+ if (screenHeight > 900) return DeviceTypes.LARGE;
50
+ return DeviceTypes.MEDIUM;
51
+ },
52
+
53
+ // Scale based on width
54
+ scale(size: number): number {
55
+ return (screenWidth / baseWidth) * size;
56
+ },
57
+
58
+ // Scale based on height
59
+ verticalScale(size: number): number {
60
+ return (screenHeight / baseHeight) * size;
61
+ },
62
+
63
+ // Moderate scale - combines width and height scaling with factor
64
+ moderateScale(size: number, factor: number = 0.5): number {
65
+ const widthScale = responsive.scale(size);
66
+ return size + (widthScale - size) * factor;
67
+ },
68
+
69
+ // Get responsive width as percentage
70
+ wp(percentage: number): number {
71
+ return (screenWidth * percentage) / 100;
72
+ },
73
+
74
+ // Get responsive height as percentage
75
+ hp(percentage: number): number {
76
+ return (screenHeight * percentage) / 100;
77
+ },
78
+
79
+ // Get responsive font size
80
+ fontSize(size: number): number {
81
+ const deviceType = responsive.getDeviceType();
82
+
83
+ switch (deviceType) {
84
+ case DeviceTypes.SMALL:
85
+ return responsive.moderateScale(size * 0.9); // 10% smaller
86
+ case DeviceTypes.LARGE:
87
+ return responsive.moderateScale(size * 1.1); // 10% larger
88
+ default:
89
+ return responsive.moderateScale(size);
90
+ }
91
+ },
92
+
93
+ // Get responsive margin/padding based on device type
94
+ getSpacing(size: number): number {
95
+ const deviceType = responsive.getDeviceType();
96
+
97
+ switch (deviceType) {
98
+ case DeviceTypes.SMALL:
99
+ return responsive.scale(size * 0.8); // 20% less spacing
100
+ case DeviceTypes.LARGE:
101
+ return responsive.scale(size * 1.2); // 20% more spacing
102
+ default:
103
+ return responsive.scale(size);
104
+ }
105
+ },
106
+
107
+ // Safe height calculation (excluding status bar and home indicator)
108
+ getSafeHeight(): number {
109
+ const statusBarHeight = Platform.OS === 'ios' ? 44 : 24;
110
+ const homeIndicatorHeight = Platform.OS === 'ios' ? 34 : 0;
111
+ return screenHeight - statusBarHeight - homeIndicatorHeight;
112
+ },
113
+
114
+ // Content height for onboarding/paywall screens
115
+ getContentHeight(): number {
116
+ const safeHeight = responsive.getSafeHeight();
117
+ const deviceType = responsive.getDeviceType();
118
+
119
+ switch (deviceType) {
120
+ case DeviceTypes.SMALL:
121
+ return safeHeight * 0.85; // More conservative for small screens
122
+ case DeviceTypes.LARGE:
123
+ return safeHeight * 0.9; // More space available
124
+ default:
125
+ return safeHeight * 0.88;
126
+ }
127
+ },
128
+
129
+ // Dynamic image container sizing
130
+ getImageContainerSize(): { width: number; height: number } {
131
+ const deviceType = responsive.getDeviceType();
132
+ const maxWidth = screenWidth - responsive.getSpacing(48);
133
+
134
+ let height: number;
135
+ switch (deviceType) {
136
+ case DeviceTypes.SMALL:
137
+ height = responsive.hp(25); // 25% of screen height
138
+ break;
139
+ case DeviceTypes.LARGE:
140
+ height = responsive.hp(28); // 30% of screen height
141
+ break;
142
+ default:
143
+ height = responsive.hp(28); // 28% of screen height
144
+ break;
145
+ }
146
+
147
+ return {
148
+ width: Math.min(maxWidth, 320),
149
+ height: Math.min(height, 280),
150
+ };
151
+ },
152
+
153
+ // Button positioning from bottom
154
+ getButtonBottomPosition(): number {
155
+ const deviceType = responsive.getDeviceType();
156
+
157
+ switch (deviceType) {
158
+ case DeviceTypes.SMALL:
159
+ return responsive.verticalScale(40); // Closer to bottom on small screens
160
+ case DeviceTypes.LARGE:
161
+ return responsive.verticalScale(80); // More space on large screens
162
+ default:
163
+ return responsive.verticalScale(60);
164
+ }
165
+ },
166
+
167
+ // Get flexible section heights for onboarding/paywall
168
+ getSectionHeights(): {
169
+ topSection: number;
170
+ bottomPadding: number;
171
+ pageIndicatorMargin: number;
172
+ } {
173
+ const deviceType = responsive.getDeviceType();
174
+ const contentHeight = responsive.getContentHeight();
175
+
176
+ switch (deviceType) {
177
+ case DeviceTypes.SMALL:
178
+ return {
179
+ topSection: contentHeight * 0.35,
180
+ bottomPadding: responsive.verticalScale(180),
181
+ pageIndicatorMargin: responsive.verticalScale(100),
182
+ };
183
+ case DeviceTypes.LARGE:
184
+ return {
185
+ topSection: contentHeight * 0.4,
186
+ bottomPadding: responsive.verticalScale(220),
187
+ pageIndicatorMargin: responsive.verticalScale(140),
188
+ };
189
+ default:
190
+ return {
191
+ topSection: contentHeight * 0.38,
192
+ bottomPadding: responsive.verticalScale(200),
193
+ pageIndicatorMargin: responsive.verticalScale(120),
194
+ };
195
+ }
196
+ },
197
+
198
+ // Get adaptive plan card height for paywall
199
+ getPlanCardHeight(): number {
200
+ const deviceType = responsive.getDeviceType();
201
+
202
+ switch (deviceType) {
203
+ case DeviceTypes.SMALL:
204
+ return responsive.verticalScale(120);
205
+ case DeviceTypes.LARGE:
206
+ return responsive.verticalScale(128);
207
+ default:
208
+ return responsive.verticalScale(124);
209
+ }
210
+ },
211
+ };
212
+
213
+ // Add spacing object after responsive is fully defined
214
+ responsive.spacing = {
215
+ xs: responsive.scale(4),
216
+ sm: responsive.scale(8),
217
+ md: responsive.scale(16),
218
+ lg: responsive.scale(24),
219
+ xl: responsive.scale(32),
220
+ xxl: responsive.scale(48),
221
+ };
222
+
223
+ // Export convenience functions
224
+ export const { wp, hp, scale, verticalScale, moderateScale, fontSize, getSpacing } = responsive;
225
+
226
+ // Export screen dimensions
227
+ export const screenDimensions = {
228
+ width: screenWidth,
229
+ height: screenHeight,
230
+ };
231
+
232
+ // Helper to check if screen is small
233
+ export const isSmallScreen = () => responsive.getDeviceType() === DeviceTypes.SMALL;
234
+ export const isLargeScreen = () => responsive.getDeviceType() === DeviceTypes.LARGE;
@@ -0,0 +1,32 @@
1
+ {
2
+ "extends": "expo/tsconfig.base",
3
+ "compilerOptions": {
4
+ "strict": true,
5
+ "esModuleInterop": true,
6
+ "allowSyntheticDefaultImports": true,
7
+ "skipLibCheck": true,
8
+ "resolveJsonModule": true,
9
+ "isolatedModules": true,
10
+ "noEmit": true,
11
+ "jsx": "react-native",
12
+ "lib": ["ES2022"],
13
+ "target": "ES2022",
14
+ "baseUrl": ".",
15
+ "paths": {
16
+ "@/*": ["./src/*"],
17
+ "@/components/*": ["./src/components/*"],
18
+ "@/hooks/*": ["./src/hooks/*"],
19
+ "@/store/*": ["./src/store/*"],
20
+ "@/services/*": ["./src/services/*"],
21
+ "@/utils/*": ["./src/utils/*"],
22
+ "@/constants/*": ["./src/constants/*"],
23
+ "@/assets/*": ["./assets/*"]
24
+ }
25
+ },
26
+ "include": [
27
+ "**/*.ts",
28
+ "**/*.tsx",
29
+ ".expo/types/**/*.ts",
30
+ "expo-env.d.ts"
31
+ ]
32
+ }
@@ -0,0 +1,26 @@
1
+ # =============================================================================
2
+ # Web App Environment Configuration
3
+ # =============================================================================
4
+ # Copy this file to .env.local and update with your actual values
5
+
6
+ # Public URL of the web app (used for OAuth callbacks)
7
+ NEXT_PUBLIC_APP_URL=http://localhost:3000
8
+
9
+ # Backend API URL (server-side only, used by server actions)
10
+ BACKEND_URL=http://localhost:8080
11
+ <% if (features.authentication.enabled) { %>
12
+
13
+ # =============================================================================
14
+ # Authentication Notes
15
+ # =============================================================================
16
+ # OAuth credentials are configured on the BACKEND, not here.
17
+ # The web app uses the BFF (Backend for Frontend) pattern:
18
+ # - Web app calls Next.js server actions
19
+ # - Server actions call Fastify backend
20
+ # - Fastify backend handles OAuth with Better Auth
21
+ #
22
+ # To configure OAuth providers, update backend/.env:
23
+ # - Google: GOOGLE_WEB_CLIENT_ID, GOOGLE_CLIENT_SECRET
24
+ # - Apple: APPLE_SERVICE_ID, APPLE_CLIENT_SECRET (Service ID is for web)
25
+ # - GitHub: GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET
26
+ <% } %>
@@ -0,0 +1,22 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema.json",
3
+ "style": "new-york",
4
+ "rsc": true,
5
+ "tsx": true,
6
+ "tailwind": {
7
+ "config": "",
8
+ "css": "src/app/globals.css",
9
+ "baseColor": "neutral",
10
+ "cssVariables": true,
11
+ "prefix": ""
12
+ },
13
+ "iconLibrary": "lucide",
14
+ "aliases": {
15
+ "components": "@/components",
16
+ "utils": "@/lib/utils",
17
+ "ui": "@/components/ui",
18
+ "lib": "@/lib",
19
+ "hooks": "@/hooks"
20
+ },
21
+ "registries": {}
22
+ }
@@ -0,0 +1,18 @@
1
+ import { defineConfig, globalIgnores } from "eslint/config";
2
+ import nextVitals from "eslint-config-next/core-web-vitals";
3
+ import nextTs from "eslint-config-next/typescript";
4
+
5
+ const eslintConfig = defineConfig([
6
+ ...nextVitals,
7
+ ...nextTs,
8
+ // Override default ignores of eslint-config-next.
9
+ globalIgnores([
10
+ // Default ignores of eslint-config-next:
11
+ ".next/**",
12
+ "out/**",
13
+ "build/**",
14
+ "next-env.d.ts",
15
+ ]),
16
+ ]);
17
+
18
+ export default eslintConfig;
@@ -0,0 +1,7 @@
1
+ import type { NextConfig } from "next";
2
+
3
+ const nextConfig: NextConfig = {
4
+ /* config options here */
5
+ };
6
+
7
+ export default nextConfig;
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "<%= projectName %>-web",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "dev": "next dev",
7
+ "build": "next build",
8
+ "start": "next start",
9
+ "lint": "eslint"
10
+ },
11
+ "dependencies": {
12
+ "@radix-ui/react-slot": "^1.2.4",
13
+ "class-variance-authority": "^0.7.1",
14
+ "clsx": "^2.1.1",
15
+ "lucide-react": "^0.562.0",
16
+ "next": "16.1.0",
17
+ "next-themes": "^0.4.6",
18
+ "@radix-ui/react-label": "^2.1.8",
19
+ "react": "19.2.3",
20
+ "react-dom": "19.2.3",
21
+ "tailwind-merge": "^3.4.0"<% if (features.authentication.enabled || features.sessionManagement) { %>,
22
+ "zustand": "^5.0.2"<% } %>
23
+ },
24
+ "devDependencies": {
25
+ "@tailwindcss/postcss": "^4",
26
+ "@types/node": "^20",
27
+ "@types/react": "^19",
28
+ "@types/react-dom": "^19",
29
+ "eslint": "^9",
30
+ "eslint-config-next": "16.1.0",
31
+ "tailwindcss": "^4",
32
+ "tw-animate-css": "^1.4.0",
33
+ "typescript": "^5"
34
+ }
35
+ }
@@ -0,0 +1,7 @@
1
+ const config = {
2
+ plugins: {
3
+ "@tailwindcss/postcss": {},
4
+ },
5
+ };
6
+
7
+ export default config;
File without changes
@@ -0,0 +1 @@
1
+ <svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
@@ -0,0 +1 @@
1
+ <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
@@ -0,0 +1 @@
1
+ <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>
@@ -0,0 +1 @@
1
+ <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
@@ -0,0 +1,152 @@
1
+ @import "tailwindcss";
2
+ @import "tw-animate-css";
3
+
4
+ @custom-variant dark (&:is(.dark *));
5
+
6
+ @theme inline {
7
+ --color-background: var(--background);
8
+ --color-foreground: var(--foreground);
9
+ --font-sans: var(--font-geist-sans);
10
+ --font-mono: var(--font-geist-mono);
11
+ --color-sidebar-ring: var(--sidebar-ring);
12
+ --color-sidebar-border: var(--sidebar-border);
13
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
14
+ --color-sidebar-accent: var(--sidebar-accent);
15
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
16
+ --color-sidebar-primary: var(--sidebar-primary);
17
+ --color-sidebar-foreground: var(--sidebar-foreground);
18
+ --color-sidebar: var(--sidebar);
19
+ --color-chart-5: var(--chart-5);
20
+ --color-chart-4: var(--chart-4);
21
+ --color-chart-3: var(--chart-3);
22
+ --color-chart-2: var(--chart-2);
23
+ --color-chart-1: var(--chart-1);
24
+ --color-ring: var(--ring);
25
+ --color-input: var(--input);
26
+ --color-border: var(--border);
27
+ --color-destructive: var(--destructive);
28
+ --color-accent-foreground: var(--accent-foreground);
29
+ --color-accent: var(--accent);
30
+ --color-muted-foreground: var(--muted-foreground);
31
+ --color-muted: var(--muted);
32
+ --color-secondary-foreground: var(--secondary-foreground);
33
+ --color-secondary: var(--secondary);
34
+ --color-primary-foreground: var(--primary-foreground);
35
+ --color-primary: var(--primary);
36
+ --color-popover-foreground: var(--popover-foreground);
37
+ --color-popover: var(--popover);
38
+ --color-card-foreground: var(--card-foreground);
39
+ --color-card: var(--card);
40
+ --radius-sm: calc(var(--radius) - 4px);
41
+ --radius-md: calc(var(--radius) - 2px);
42
+ --radius-lg: var(--radius);
43
+ --radius-xl: calc(var(--radius) + 4px);
44
+ }
45
+
46
+ :root {
47
+ --radius: 0.75rem;
48
+
49
+ /* Premium Neutral (True Black/White feel) */
50
+ --background: #ffffff;
51
+ --foreground: #09090b;
52
+
53
+ --card: #ffffff;
54
+ --card-foreground: #09090b;
55
+
56
+ --popover: #ffffff;
57
+ --popover-foreground: #09090b;
58
+
59
+ --primary: #18181b;
60
+ --primary-foreground: #fafafa;
61
+
62
+ --secondary: #f4f4f5;
63
+ --secondary-foreground: #18181b;
64
+
65
+ --muted: #f4f4f5;
66
+ --muted-foreground: #71717a;
67
+
68
+ --accent: #f4f4f5;
69
+ --accent-foreground: #18181b;
70
+
71
+ --destructive: #ef4444;
72
+ --destructive-foreground: #fafafa;
73
+
74
+ --border: #e4e4e7;
75
+ --input: #e4e4e7;
76
+ --ring: #18181b;
77
+
78
+ /* Charts */
79
+ --chart-1: #18181b;
80
+ --chart-2: #27272a;
81
+ --chart-3: #52525b;
82
+ --chart-4: #a1a1aa;
83
+ --chart-5: #e4e4e7;
84
+
85
+ /* Sidebar */
86
+ --sidebar: #f9fafb;
87
+ --sidebar-foreground: #18181b;
88
+ --sidebar-primary: #18181b;
89
+ --sidebar-primary-foreground: #fafafa;
90
+ --sidebar-accent: #f4f4f5;
91
+ --sidebar-accent-foreground: #18181b;
92
+ --sidebar-border: #e4e4e7;
93
+ --sidebar-ring: #d4d4d8;
94
+ }
95
+
96
+ .dark {
97
+ /* Premium Dark (Deep, Pure Black/Gray, no blue tint) */
98
+ --background: #09090b;
99
+ --foreground: #fafafa;
100
+
101
+ --card: #09090b;
102
+ --card-foreground: #fafafa;
103
+
104
+ --popover: #09090b;
105
+ --popover-foreground: #fafafa;
106
+
107
+ --primary: #fafafa;
108
+ --primary-foreground: #18181b;
109
+
110
+ --secondary: #27272a;
111
+ --secondary-foreground: #fafafa;
112
+
113
+ --muted: #27272a;
114
+ --muted-foreground: #a1a1aa;
115
+
116
+ --accent: #27272a;
117
+ --accent-foreground: #fafafa;
118
+
119
+ --destructive: #7f1d1d;
120
+ --destructive-foreground: #fafafa;
121
+
122
+ --border: #27272a;
123
+ --input: #27272a;
124
+ --ring: #52525b;
125
+
126
+ /* Charts */
127
+ --chart-1: #22c55e;
128
+ --chart-2: #3b82f6;
129
+ --chart-3: #f59e0b;
130
+ --chart-4: #ef4444;
131
+ --chart-5: #8b5cf6;
132
+
133
+ /* Sidebar */
134
+ --sidebar: #09090b;
135
+ --sidebar-foreground: #fafafa;
136
+ --sidebar-primary: #3b82f6;
137
+ --sidebar-primary-foreground: #ffffff;
138
+ --sidebar-accent: #27272a;
139
+ --sidebar-accent-foreground: #fafafa;
140
+ --sidebar-border: #27272a;
141
+ --sidebar-ring: #3b82f6;
142
+ }
143
+
144
+ @layer base {
145
+ * {
146
+ @apply border-border outline-ring/50;
147
+ }
148
+
149
+ body {
150
+ @apply bg-background text-foreground;
151
+ }
152
+ }
@@ -0,0 +1,54 @@
1
+ import type { Metadata } from "next";
2
+ import { Geist, Geist_Mono } from "next/font/google";
3
+ import "./globals.css";
4
+ import { ThemeProvider } from "@/components/providers/theme-provider";
5
+ <% if (features.authentication.enabled) { %>
6
+ import { AuthHydrator } from "@/components/auth/auth-hydrator";
7
+ import { getSession } from "@/lib/auth/actions";
8
+ <% } %>
9
+ <% if (features.sessionManagement) { %>
10
+ import { DeviceSessionSetup } from "@/components/providers/device-session-setup";
11
+ <% } %>
12
+
13
+ const geistSans = Geist({
14
+ variable: "--font-geist-sans",
15
+ subsets: ["latin"],
16
+ });
17
+
18
+ const geistMono = Geist_Mono({
19
+ variable: "--font-geist-mono",
20
+ subsets: ["latin"],
21
+ });
22
+
23
+ export const metadata: Metadata = {
24
+ title: "<%= projectName %>",
25
+ description: "Generated by create-stackr",
26
+ };
27
+
28
+ export default async function RootLayout({
29
+ children,
30
+ }: Readonly<{
31
+ children: React.ReactNode;
32
+ }>) {
33
+ <% if (features.authentication.enabled) { %>
34
+ const session = await getSession();
35
+ <% } %>
36
+
37
+ return (
38
+ <html lang="en" suppressHydrationWarning>
39
+ <body
40
+ className={`${geistSans.variable} ${geistMono.variable} antialiased`}
41
+ >
42
+ <ThemeProvider>
43
+ <% if (features.authentication.enabled) { %>
44
+ <AuthHydrator session={session} />
45
+ <% } %>
46
+ <% if (features.sessionManagement) { %>
47
+ <DeviceSessionSetup />
48
+ <% } %>
49
+ {children}
50
+ </ThemeProvider>
51
+ </body>
52
+ </html>
53
+ );
54
+ }