tvi-cli 0.1.4

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 (306) hide show
  1. package/README.md +147 -0
  2. package/dist/index.js +4315 -0
  3. package/package.json +75 -0
  4. package/templates/addons/biome/biome.json.hbs +83 -0
  5. package/templates/addons/husky/.husky/pre-commit +1 -0
  6. package/templates/addons/pwa/apps/web/next/public/favicon/apple-touch-icon.png +0 -0
  7. package/templates/addons/pwa/apps/web/next/public/favicon/favicon-96x96.png +0 -0
  8. package/templates/addons/pwa/apps/web/next/public/favicon/favicon.svg +6 -0
  9. package/templates/addons/pwa/apps/web/next/public/favicon/site.webmanifest.hbs +21 -0
  10. package/templates/addons/pwa/apps/web/next/public/favicon/web-app-manifest-192x192.png +0 -0
  11. package/templates/addons/pwa/apps/web/next/public/favicon/web-app-manifest-512x512.png +0 -0
  12. package/templates/addons/pwa/apps/web/next/src/app/manifest.ts.hbs +26 -0
  13. package/templates/addons/pwa/apps/web/vite/public/logo.png +0 -0
  14. package/templates/addons/pwa/apps/web/vite/pwa-assets.config.ts.hbs +12 -0
  15. package/templates/addons/turborepo/turbo.json.hbs +43 -0
  16. package/templates/api/orpc/native/utils/orpc.ts.hbs +35 -0
  17. package/templates/api/orpc/server/base/src/lib/context.ts.hbs +125 -0
  18. package/templates/api/orpc/server/base/src/lib/orpc.ts.hbs +21 -0
  19. package/templates/api/orpc/server/next/src/app/rpc/[...all]/route.ts.hbs +23 -0
  20. package/templates/api/orpc/web/nuxt/app/plugins/orpc.ts.hbs +35 -0
  21. package/templates/api/orpc/web/react/base/src/utils/orpc.ts.hbs +42 -0
  22. package/templates/api/orpc/web/solid/src/utils/orpc.ts.hbs +30 -0
  23. package/templates/api/orpc/web/svelte/src/lib/orpc.ts.hbs +31 -0
  24. package/templates/api/trpc/native/utils/trpc.ts.hbs +32 -0
  25. package/templates/api/trpc/server/base/src/lib/context.ts.hbs +127 -0
  26. package/templates/api/trpc/server/base/src/lib/trpc.ts.hbs +26 -0
  27. package/templates/api/trpc/server/next/src/app/trpc/[trpc]/route.ts +14 -0
  28. package/templates/api/trpc/web/react/base/src/utils/trpc.ts.hbs +97 -0
  29. package/templates/auth/native/native-base/lib/auth-client.ts.hbs +13 -0
  30. package/templates/auth/native/nativewind/app/(drawer)/index.tsx.hbs +95 -0
  31. package/templates/auth/native/nativewind/components/sign-in.tsx.hbs +93 -0
  32. package/templates/auth/native/nativewind/components/sign-up.tsx.hbs +104 -0
  33. package/templates/auth/native/unistyles/app/(drawer)/index.tsx.hbs +179 -0
  34. package/templates/auth/native/unistyles/components/sign-in.tsx.hbs +134 -0
  35. package/templates/auth/native/unistyles/components/sign-up.tsx.hbs +152 -0
  36. package/templates/auth/server/base/src/lib/auth.ts.hbs +141 -0
  37. package/templates/auth/server/db/drizzle/mysql/src/db/schema/auth.ts +58 -0
  38. package/templates/auth/server/db/drizzle/postgres/src/db/schema/auth.ts +47 -0
  39. package/templates/auth/server/db/drizzle/sqlite/src/db/schema/auth.ts +55 -0
  40. package/templates/auth/server/db/mongoose/mongodb/src/db/models/auth.model.ts +68 -0
  41. package/templates/auth/server/db/prisma/mongodb/prisma/schema/auth.prisma +59 -0
  42. package/templates/auth/server/db/prisma/mysql/prisma/schema/auth.prisma +59 -0
  43. package/templates/auth/server/db/prisma/postgres/prisma/schema/auth.prisma +59 -0
  44. package/templates/auth/server/db/prisma/sqlite/prisma/schema/auth.prisma +59 -0
  45. package/templates/auth/server/next/src/app/api/auth/[...all]/route.ts +4 -0
  46. package/templates/auth/web/nuxt/app/components/SignInForm.vue +77 -0
  47. package/templates/auth/web/nuxt/app/components/SignUpForm.vue +84 -0
  48. package/templates/auth/web/nuxt/app/components/UserMenu.vue +42 -0
  49. package/templates/auth/web/nuxt/app/middleware/auth.ts +12 -0
  50. package/templates/auth/web/nuxt/app/pages/dashboard.vue +27 -0
  51. package/templates/auth/web/nuxt/app/pages/login.vue +24 -0
  52. package/templates/auth/web/nuxt/app/plugins/auth-client.ts +16 -0
  53. package/templates/auth/web/react/base/src/lib/auth-client.ts.hbs +10 -0
  54. package/templates/auth/web/react/next/src/app/dashboard/page.tsx.hbs +47 -0
  55. package/templates/auth/web/react/next/src/app/login/page.tsx +16 -0
  56. package/templates/auth/web/react/next/src/components/sign-in-form.tsx +135 -0
  57. package/templates/auth/web/react/next/src/components/sign-up-form.tsx +160 -0
  58. package/templates/auth/web/react/next/src/components/theme-provider.tsx +11 -0
  59. package/templates/auth/web/react/next/src/components/user-menu.tsx +60 -0
  60. package/templates/auth/web/react/react-router/src/components/sign-in-form.tsx +135 -0
  61. package/templates/auth/web/react/react-router/src/components/sign-up-form.tsx +160 -0
  62. package/templates/auth/web/react/react-router/src/components/user-menu.tsx +60 -0
  63. package/templates/auth/web/react/react-router/src/routes/dashboard.tsx.hbs +40 -0
  64. package/templates/auth/web/react/react-router/src/routes/login.tsx +13 -0
  65. package/templates/auth/web/react/tanstack-router/src/components/sign-in-form.tsx +139 -0
  66. package/templates/auth/web/react/tanstack-router/src/components/sign-up-form.tsx +164 -0
  67. package/templates/auth/web/react/tanstack-router/src/components/user-menu.tsx +62 -0
  68. package/templates/auth/web/react/tanstack-router/src/routes/dashboard.tsx.hbs +47 -0
  69. package/templates/auth/web/react/tanstack-router/src/routes/login.tsx +18 -0
  70. package/templates/auth/web/react/tanstack-start/src/components/sign-in-form.tsx +139 -0
  71. package/templates/auth/web/react/tanstack-start/src/components/sign-up-form.tsx +164 -0
  72. package/templates/auth/web/react/tanstack-start/src/components/user-menu.tsx +62 -0
  73. package/templates/auth/web/react/tanstack-start/src/routes/dashboard.tsx.hbs +51 -0
  74. package/templates/auth/web/react/tanstack-start/src/routes/login.tsx +18 -0
  75. package/templates/auth/web/solid/src/components/sign-in-form.tsx +132 -0
  76. package/templates/auth/web/solid/src/components/sign-up-form.tsx +158 -0
  77. package/templates/auth/web/solid/src/components/user-menu.tsx.hbs +55 -0
  78. package/templates/auth/web/solid/src/lib/auth-client.ts +5 -0
  79. package/templates/auth/web/solid/src/routes/dashboard.tsx +38 -0
  80. package/templates/auth/web/solid/src/routes/login.tsx +23 -0
  81. package/templates/auth/web/svelte/src/components/SignInForm.svelte +108 -0
  82. package/templates/auth/web/svelte/src/components/SignUpForm.svelte +142 -0
  83. package/templates/auth/web/svelte/src/components/UserMenu.svelte +54 -0
  84. package/templates/auth/web/svelte/src/lib/auth-client.ts +6 -0
  85. package/templates/auth/web/svelte/src/routes/dashboard/+page.svelte +31 -0
  86. package/templates/auth/web/svelte/src/routes/login/+page.svelte +12 -0
  87. package/templates/backend/convex/packages/backend/_gitignore +2 -0
  88. package/templates/backend/convex/packages/backend/convex/README.md +90 -0
  89. package/templates/backend/convex/packages/backend/convex/healthCheck.ts +7 -0
  90. package/templates/backend/convex/packages/backend/convex/schema.ts +9 -0
  91. package/templates/backend/convex/packages/backend/convex/todos.ts +42 -0
  92. package/templates/backend/convex/packages/backend/convex/tsconfig.json +25 -0
  93. package/templates/backend/convex/packages/backend/package.json.hbs +17 -0
  94. package/templates/backend/server/elysia/src/index.ts.hbs +72 -0
  95. package/templates/backend/server/express/src/index.ts.hbs +88 -0
  96. package/templates/backend/server/fastify/src/index.ts.hbs +155 -0
  97. package/templates/backend/server/hono/src/index.ts.hbs +133 -0
  98. package/templates/backend/server/next/next-env.d.ts +5 -0
  99. package/templates/backend/server/next/next.config.ts +7 -0
  100. package/templates/backend/server/next/package.json.hbs +24 -0
  101. package/templates/backend/server/next/src/app/route.ts +5 -0
  102. package/templates/backend/server/next/src/middleware.ts +19 -0
  103. package/templates/backend/server/next/tsconfig.json.hbs +33 -0
  104. package/templates/backend/server/server-base/_gitignore +52 -0
  105. package/templates/backend/server/server-base/package.json.hbs +28 -0
  106. package/templates/backend/server/server-base/src/routers/index.ts.hbs +53 -0
  107. package/templates/backend/server/server-base/tsconfig.json.hbs +39 -0
  108. package/templates/base/_gitignore +2 -0
  109. package/templates/base/package.json.hbs +11 -0
  110. package/templates/db/drizzle/mysql/drizzle.config.ts.hbs +10 -0
  111. package/templates/db/drizzle/mysql/src/db/index.ts.hbs +20 -0
  112. package/templates/db/drizzle/postgres/drizzle.config.ts.hbs +10 -0
  113. package/templates/db/drizzle/postgres/src/db/index.ts.hbs +12 -0
  114. package/templates/db/drizzle/sqlite/drizzle.config.ts.hbs +24 -0
  115. package/templates/db/drizzle/sqlite/src/db/index.ts.hbs +35 -0
  116. package/templates/db/mongoose/mongodb/src/db/index.ts.hbs +9 -0
  117. package/templates/db/prisma/mongodb/prisma/index.ts.hbs +5 -0
  118. package/templates/db/prisma/mongodb/prisma/schema/schema.prisma +10 -0
  119. package/templates/db/prisma/mongodb/prisma.config.ts.hbs +8 -0
  120. package/templates/db/prisma/mysql/prisma/index.ts +5 -0
  121. package/templates/db/prisma/mysql/prisma/schema/schema.prisma +10 -0
  122. package/templates/db/prisma/mysql/prisma.config.ts +8 -0
  123. package/templates/db/prisma/postgres/prisma/index.ts +5 -0
  124. package/templates/db/prisma/postgres/prisma/schema/schema.prisma.hbs +13 -0
  125. package/templates/db/prisma/postgres/prisma.config.ts.hbs +12 -0
  126. package/templates/db/prisma/sqlite/prisma/index.ts +5 -0
  127. package/templates/db/prisma/sqlite/prisma/schema/schema.prisma +10 -0
  128. package/templates/db/prisma/sqlite/prisma.config.ts +8 -0
  129. package/templates/examples/ai/native/nativewind/app/(drawer)/ai.tsx.hbs +155 -0
  130. package/templates/examples/ai/native/nativewind/polyfills.js +25 -0
  131. package/templates/examples/ai/native/unistyles/app/(drawer)/ai.tsx.hbs +279 -0
  132. package/templates/examples/ai/native/unistyles/polyfills.js +25 -0
  133. package/templates/examples/ai/server/next/src/app/ai/route.ts +15 -0
  134. package/templates/examples/ai/web/nuxt/app/pages/ai.vue +63 -0
  135. package/templates/examples/ai/web/react/next/src/app/ai/page.tsx +67 -0
  136. package/templates/examples/ai/web/react/react-router/src/routes/ai.tsx +64 -0
  137. package/templates/examples/ai/web/react/tanstack-router/src/routes/ai.tsx +69 -0
  138. package/templates/examples/ai/web/react/tanstack-start/src/routes/ai.tsx +69 -0
  139. package/templates/examples/ai/web/svelte/src/routes/ai/+page.svelte +98 -0
  140. package/templates/examples/todo/native/nativewind/app/(drawer)/todos.tsx.hbs +295 -0
  141. package/templates/examples/todo/native/unistyles/app/(drawer)/todos.tsx.hbs +340 -0
  142. package/templates/examples/todo/server/drizzle/base/src/routers/todo.ts.hbs +79 -0
  143. package/templates/examples/todo/server/drizzle/mysql/src/db/schema/todo.ts +7 -0
  144. package/templates/examples/todo/server/drizzle/postgres/src/db/schema/todo.ts +7 -0
  145. package/templates/examples/todo/server/drizzle/sqlite/src/db/schema/todo.ts +7 -0
  146. package/templates/examples/todo/server/mongoose/base/src/routers/todo.ts.hbs +66 -0
  147. package/templates/examples/todo/server/mongoose/mongodb/src/db/models/todo.model.ts +24 -0
  148. package/templates/examples/todo/server/prisma/base/src/routers/todo.ts.hbs +118 -0
  149. package/templates/examples/todo/server/prisma/mongodb/prisma/schema/todo.prisma +7 -0
  150. package/templates/examples/todo/server/prisma/mysql/prisma/schema/todo.prisma +7 -0
  151. package/templates/examples/todo/server/prisma/postgres/prisma/schema/todo.prisma +7 -0
  152. package/templates/examples/todo/server/prisma/sqlite/prisma/schema/todo.prisma +7 -0
  153. package/templates/examples/todo/web/nuxt/app/pages/todos.vue +108 -0
  154. package/templates/examples/todo/web/react/next/src/app/todos/page.tsx.hbs +245 -0
  155. package/templates/examples/todo/web/react/react-router/src/routes/todos.tsx.hbs +242 -0
  156. package/templates/examples/todo/web/react/tanstack-router/src/routes/todos.tsx.hbs +247 -0
  157. package/templates/examples/todo/web/react/tanstack-start/src/routes/todos.tsx.hbs +268 -0
  158. package/templates/examples/todo/web/solid/src/routes/todos.tsx.hbs +132 -0
  159. package/templates/examples/todo/web/svelte/src/routes/todos/+page.svelte.hbs +317 -0
  160. package/templates/extras/_npmrc.hbs +5 -0
  161. package/templates/extras/pnpm-workspace.yaml +3 -0
  162. package/templates/frontend/native/native-base/assets/adaptive-icon.png +0 -0
  163. package/templates/frontend/native/native-base/assets/favicon.png +0 -0
  164. package/templates/frontend/native/native-base/assets/icon.png +0 -0
  165. package/templates/frontend/native/native-base/assets/splash.png +0 -0
  166. package/templates/frontend/native/nativewind/_gitignore +25 -0
  167. package/templates/frontend/native/nativewind/app/(drawer)/(tabs)/_layout.tsx +46 -0
  168. package/templates/frontend/native/nativewind/app/(drawer)/(tabs)/index.tsx +19 -0
  169. package/templates/frontend/native/nativewind/app/(drawer)/(tabs)/two.tsx +19 -0
  170. package/templates/frontend/native/nativewind/app/(drawer)/_layout.tsx.hbs +67 -0
  171. package/templates/frontend/native/nativewind/app/(drawer)/index.tsx.hbs +95 -0
  172. package/templates/frontend/native/nativewind/app/+html.tsx +47 -0
  173. package/templates/frontend/native/nativewind/app/+not-found.tsx +29 -0
  174. package/templates/frontend/native/nativewind/app/_layout.tsx.hbs +126 -0
  175. package/templates/frontend/native/nativewind/app/modal.tsx +14 -0
  176. package/templates/frontend/native/nativewind/app-env.d.ts +2 -0
  177. package/templates/frontend/native/nativewind/app.json +46 -0
  178. package/templates/frontend/native/nativewind/babel.config.js +11 -0
  179. package/templates/frontend/native/nativewind/components/container.tsx +8 -0
  180. package/templates/frontend/native/nativewind/components/header-button.tsx +26 -0
  181. package/templates/frontend/native/nativewind/components/tabbar-icon.tsx +8 -0
  182. package/templates/frontend/native/nativewind/global.css +50 -0
  183. package/templates/frontend/native/nativewind/lib/android-navigation-bar.tsx +11 -0
  184. package/templates/frontend/native/nativewind/lib/constants.ts +18 -0
  185. package/templates/frontend/native/nativewind/lib/use-color-scheme.ts +12 -0
  186. package/templates/frontend/native/nativewind/metro.config.js +59 -0
  187. package/templates/frontend/native/nativewind/package.json.hbs +49 -0
  188. package/templates/frontend/native/nativewind/tailwind.config.js +59 -0
  189. package/templates/frontend/native/nativewind/tsconfig.json.hbs +23 -0
  190. package/templates/frontend/native/unistyles/_gitignore +24 -0
  191. package/templates/frontend/native/unistyles/app/(drawer)/(tabs)/_layout.tsx +39 -0
  192. package/templates/frontend/native/unistyles/app/(drawer)/(tabs)/index.tsx +37 -0
  193. package/templates/frontend/native/unistyles/app/(drawer)/(tabs)/two.tsx +37 -0
  194. package/templates/frontend/native/unistyles/app/(drawer)/_layout.tsx.hbs +87 -0
  195. package/templates/frontend/native/unistyles/app/(drawer)/index.tsx.hbs +194 -0
  196. package/templates/frontend/native/unistyles/app/+html.tsx +48 -0
  197. package/templates/frontend/native/unistyles/app/+not-found.tsx +65 -0
  198. package/templates/frontend/native/unistyles/app/_layout.tsx.hbs +104 -0
  199. package/templates/frontend/native/unistyles/app/modal.tsx +33 -0
  200. package/templates/frontend/native/unistyles/app.json +44 -0
  201. package/templates/frontend/native/unistyles/babel.config.js +20 -0
  202. package/templates/frontend/native/unistyles/breakpoints.ts +9 -0
  203. package/templates/frontend/native/unistyles/components/container.tsx +15 -0
  204. package/templates/frontend/native/unistyles/components/header-button.tsx +36 -0
  205. package/templates/frontend/native/unistyles/components/tabbar-icon.tsx +8 -0
  206. package/templates/frontend/native/unistyles/expo-env.d.ts +3 -0
  207. package/templates/frontend/native/unistyles/index.js +2 -0
  208. package/templates/frontend/native/unistyles/metro.config.js +20 -0
  209. package/templates/frontend/native/unistyles/package.json.hbs +50 -0
  210. package/templates/frontend/native/unistyles/theme.ts +98 -0
  211. package/templates/frontend/native/unistyles/tsconfig.json.hbs +17 -0
  212. package/templates/frontend/native/unistyles/unistyles.ts +27 -0
  213. package/templates/frontend/nuxt/_gitignore +24 -0
  214. package/templates/frontend/nuxt/app/app.config.ts +15 -0
  215. package/templates/frontend/nuxt/app/app.vue +13 -0
  216. package/templates/frontend/nuxt/app/assets/css/main.css +2 -0
  217. package/templates/frontend/nuxt/app/components/Header.vue.hbs +45 -0
  218. package/templates/frontend/nuxt/app/components/Loader.vue +5 -0
  219. package/templates/frontend/nuxt/app/components/ModeToggle.vue +23 -0
  220. package/templates/frontend/nuxt/app/layouts/default.vue.hbs +11 -0
  221. package/templates/frontend/nuxt/app/pages/index.vue.hbs +57 -0
  222. package/templates/frontend/nuxt/app/plugins/vue-query.ts.hbs +44 -0
  223. package/templates/frontend/nuxt/nuxt.config.ts.hbs +19 -0
  224. package/templates/frontend/nuxt/package.json.hbs +25 -0
  225. package/templates/frontend/nuxt/public/favicon.ico +0 -0
  226. package/templates/frontend/nuxt/public/robots.txt +2 -0
  227. package/templates/frontend/nuxt/server/tsconfig.json +3 -0
  228. package/templates/frontend/nuxt/tsconfig.json.hbs +9 -0
  229. package/templates/frontend/react/next/next-env.d.ts.hbs +5 -0
  230. package/templates/frontend/react/next/next.config.ts.hbs +5 -0
  231. package/templates/frontend/react/next/package.json.hbs +34 -0
  232. package/templates/frontend/react/next/postcss.config.mjs.hbs +5 -0
  233. package/templates/frontend/react/next/src/app/favicon.ico +0 -0
  234. package/templates/frontend/react/next/src/app/layout.tsx.hbs +41 -0
  235. package/templates/frontend/react/next/src/app/page.tsx.hbs +68 -0
  236. package/templates/frontend/react/next/src/components/mode-toggle.tsx.hbs +39 -0
  237. package/templates/frontend/react/next/src/components/providers.tsx.hbs +56 -0
  238. package/templates/frontend/react/next/src/components/theme-provider.tsx.hbs +11 -0
  239. package/templates/frontend/react/next/tsconfig.json.hbs +33 -0
  240. package/templates/frontend/react/react-router/package.json.hbs +42 -0
  241. package/templates/frontend/react/react-router/public/favicon.ico +0 -0
  242. package/templates/frontend/react/react-router/react-router.config.ts +6 -0
  243. package/templates/frontend/react/react-router/src/components/mode-toggle.tsx +37 -0
  244. package/templates/frontend/react/react-router/src/components/theme-provider.tsx +73 -0
  245. package/templates/frontend/react/react-router/src/root.tsx.hbs +152 -0
  246. package/templates/frontend/react/react-router/src/routes/_index.tsx.hbs +74 -0
  247. package/templates/frontend/react/react-router/src/routes.ts +4 -0
  248. package/templates/frontend/react/react-router/tsconfig.json.hbs +32 -0
  249. package/templates/frontend/react/react-router/vite.config.ts.hbs +33 -0
  250. package/templates/frontend/react/tanstack-router/index.html +12 -0
  251. package/templates/frontend/react/tanstack-router/package.json.hbs +41 -0
  252. package/templates/frontend/react/tanstack-router/src/components/mode-toggle.tsx +37 -0
  253. package/templates/frontend/react/tanstack-router/src/components/theme-provider.tsx +73 -0
  254. package/templates/frontend/react/tanstack-router/src/main.tsx.hbs +66 -0
  255. package/templates/frontend/react/tanstack-router/src/routes/__root.tsx.hbs +100 -0
  256. package/templates/frontend/react/tanstack-router/src/routes/index.tsx.hbs +74 -0
  257. package/templates/frontend/react/tanstack-router/tsconfig.json.hbs +23 -0
  258. package/templates/frontend/react/tanstack-router/vite.config.ts.hbs +39 -0
  259. package/templates/frontend/react/tanstack-start/package.json.hbs +44 -0
  260. package/templates/frontend/react/tanstack-start/public/robots.txt +3 -0
  261. package/templates/frontend/react/tanstack-start/src/router.tsx.hbs +144 -0
  262. package/templates/frontend/react/tanstack-start/src/routes/__root.tsx.hbs +97 -0
  263. package/templates/frontend/react/tanstack-start/src/routes/index.tsx.hbs +74 -0
  264. package/templates/frontend/react/tanstack-start/tsconfig.json.hbs +33 -0
  265. package/templates/frontend/react/tanstack-start/vite.config.ts +8 -0
  266. package/templates/frontend/react/web-base/_gitignore +52 -0
  267. package/templates/frontend/react/web-base/components.json +21 -0
  268. package/templates/frontend/react/web-base/src/components/header.tsx.hbs +79 -0
  269. package/templates/frontend/react/web-base/src/components/loader.tsx +9 -0
  270. package/templates/frontend/react/web-base/src/components/ui/button.tsx +59 -0
  271. package/templates/frontend/react/web-base/src/components/ui/card.tsx +92 -0
  272. package/templates/frontend/react/web-base/src/components/ui/checkbox.tsx +30 -0
  273. package/templates/frontend/react/web-base/src/components/ui/dropdown-menu.tsx +257 -0
  274. package/templates/frontend/react/web-base/src/components/ui/input.tsx +21 -0
  275. package/templates/frontend/react/web-base/src/components/ui/label.tsx +22 -0
  276. package/templates/frontend/react/web-base/src/components/ui/skeleton.tsx +13 -0
  277. package/templates/frontend/react/web-base/src/components/ui/sonner.tsx +25 -0
  278. package/templates/frontend/react/web-base/src/index.css +134 -0
  279. package/templates/frontend/react/web-base/src/lib/utils.ts +6 -0
  280. package/templates/frontend/solid/_gitignore +7 -0
  281. package/templates/frontend/solid/index.html +13 -0
  282. package/templates/frontend/solid/package.json.hbs +27 -0
  283. package/templates/frontend/solid/public/robots.txt +3 -0
  284. package/templates/frontend/solid/src/components/header.tsx.hbs +38 -0
  285. package/templates/frontend/solid/src/components/loader.tsx +9 -0
  286. package/templates/frontend/solid/src/main.tsx.hbs +38 -0
  287. package/templates/frontend/solid/src/routes/__root.tsx.hbs +34 -0
  288. package/templates/frontend/solid/src/routes/index.tsx.hbs +61 -0
  289. package/templates/frontend/solid/src/styles.css +5 -0
  290. package/templates/frontend/solid/tsconfig.json.hbs +34 -0
  291. package/templates/frontend/solid/vite.config.js.hbs +39 -0
  292. package/templates/frontend/svelte/_gitignore +23 -0
  293. package/templates/frontend/svelte/_npmrc +1 -0
  294. package/templates/frontend/svelte/package.json.hbs +31 -0
  295. package/templates/frontend/svelte/src/app.css +5 -0
  296. package/templates/frontend/svelte/src/app.d.ts +13 -0
  297. package/templates/frontend/svelte/src/app.html +12 -0
  298. package/templates/frontend/svelte/src/components/Header.svelte.hbs +40 -0
  299. package/templates/frontend/svelte/src/lib/index.ts +1 -0
  300. package/templates/frontend/svelte/src/routes/+layout.svelte.hbs +54 -0
  301. package/templates/frontend/svelte/src/routes/+page.svelte.hbs +72 -0
  302. package/templates/frontend/svelte/static/favicon.png +0 -0
  303. package/templates/frontend/svelte/svelte.config.js +18 -0
  304. package/templates/frontend/svelte/tsconfig.json.hbs +24 -0
  305. package/templates/frontend/svelte/vite.config.ts +7 -0
  306. package/templates/runtime/workers/apps/server/wrangler.jsonc.hbs +34 -0
@@ -0,0 +1,317 @@
1
+ {{#if (eq backend "convex")}}
2
+ <script lang="ts">
3
+ import { useQuery, useConvexClient } from 'convex-svelte';
4
+ import { api } from '@{{projectName}}/backend/convex/_generated/api';
5
+ import type { Id } from '@{{projectName}}/backend/convex/_generated/dataModel';
6
+
7
+ let newTodoText = $state('');
8
+ let isAdding = $state(false);
9
+ let addError = $state<Error | null>(null);
10
+ let togglingId = $state<Id<'todos'> | null>(null);
11
+ let toggleError = $state<Error | null>(null);
12
+ let deletingId = $state<Id<'todos'> | null>(null);
13
+ let deleteError = $state<Error | null>(null);
14
+
15
+ const client = useConvexClient();
16
+
17
+ const todosQuery = useQuery(api.todos.getAll, {});
18
+
19
+ async function handleAddTodo(event: SubmitEvent) {
20
+ event.preventDefault();
21
+ const text = newTodoText.trim();
22
+ if (!text || isAdding) return;
23
+
24
+ isAdding = true;
25
+ addError = null;
26
+ try {
27
+ await client.mutation(api.todos.create, { text });
28
+ newTodoText = '';
29
+ } catch (err) {
30
+ console.error('Failed to add todo:', err);
31
+ addError = err instanceof Error ? err : new Error(String(err));
32
+ } finally {
33
+ isAdding = false;
34
+ }
35
+ }
36
+
37
+ async function handleToggleTodo(id: Id<'todos'>, completed: boolean) {
38
+ if (togglingId === id || deletingId === id) return;
39
+
40
+ togglingId = id;
41
+ toggleError = null;
42
+ try {
43
+ await client.mutation(api.todos.toggle, { id, completed: !completed });
44
+ } catch (err) {
45
+ console.error('Failed to toggle todo:', err);
46
+ toggleError = err instanceof Error ? err : new Error(String(err));
47
+ } finally {
48
+ if (togglingId === id) {
49
+ togglingId = null;
50
+ }
51
+ }
52
+ }
53
+
54
+ async function handleDeleteTodo(id: Id<'todos'>) {
55
+ if (togglingId === id || deletingId === id) return;
56
+
57
+ deletingId = id;
58
+ deleteError = null;
59
+ try {
60
+ await client.mutation(api.todos.deleteTodo, { id });
61
+ } catch (err) {
62
+ console.error('Failed to delete todo:', err);
63
+ deleteError = err instanceof Error ? err : new Error(String(err));
64
+ } finally {
65
+ if (deletingId === id) {
66
+ deletingId = null;
67
+ }
68
+ }
69
+ }
70
+
71
+ const canAdd = $derived(!isAdding && newTodoText.trim().length > 0);
72
+ const isLoadingTodos = $derived(todosQuery.isLoading);
73
+ const todos = $derived(todosQuery.data ?? []);
74
+ const hasTodos = $derived(todos.length > 0);
75
+
76
+ </script>
77
+
78
+ <div class="p-4">
79
+ <h1 class="text-xl mb-4">Todos (Convex)</h1>
80
+
81
+ <form onsubmit={handleAddTodo} class="flex gap-2 mb-4">
82
+ <input
83
+ type="text"
84
+ bind:value={newTodoText}
85
+ placeholder="New task..."
86
+ disabled={isAdding}
87
+ class="p-1 flex-grow"
88
+ />
89
+ <button
90
+ type="submit"
91
+ disabled={!canAdd}
92
+ class="bg-blue-500 text-white px-3 py-1 rounded disabled:opacity-50"
93
+ >
94
+ {#if isAdding}Adding...{:else}Add{/if}
95
+ </button>
96
+ </form>
97
+
98
+ {#if isLoadingTodos}
99
+ <p>Loading...</p>
100
+ {:else if !hasTodos}
101
+ <p>No todos yet.</p>
102
+ {:else}
103
+ <ul class="space-y-1">
104
+ {#each todos as todo (todo._id)}
105
+ {@const isTogglingThis = togglingId === todo._id}
106
+ {@const isDeletingThis = deletingId === todo._id}
107
+ {@const isDisabled = isTogglingThis || isDeletingThis}
108
+ <li
109
+ class="flex items-center justify-between p-2"
110
+ class:opacity-50={isDisabled}
111
+ >
112
+ <div class="flex items-center gap-2">
113
+ <input
114
+ type="checkbox"
115
+ id={`todo-${todo._id}`}
116
+ checked={todo.completed}
117
+ onchange={() => handleToggleTodo(todo._id, todo.completed)}
118
+ disabled={isDisabled}
119
+ />
120
+ <label
121
+ for={`todo-${todo._id}`}
122
+ class:line-through={todo.completed}
123
+ >
124
+ {todo.text}
125
+ </label>
126
+ </div>
127
+ <button
128
+ type="button"
129
+ onclick={() => handleDeleteTodo(todo._id)}
130
+ disabled={isDisabled}
131
+ aria-label="Delete todo"
132
+ class="text-red-500 px-1 disabled:opacity-50"
133
+ >
134
+ {#if isDeletingThis}Deleting...{:else}X{/if}
135
+ </button>
136
+ </li>
137
+ {/each}
138
+ </ul>
139
+ {/if}
140
+
141
+ {#if todosQuery.error}
142
+ <p class="mt-4 text-red-500">
143
+ Error loading: {todosQuery.error?.message ?? 'Unknown error'}
144
+ </p>
145
+ {/if}
146
+ {#if addError}
147
+ <p class="mt-4 text-red-500">
148
+ Error adding: {addError.message ?? 'Unknown error'}
149
+ </p>
150
+ {/if}
151
+ {#if toggleError}
152
+ <p class="mt-4 text-red-500">
153
+ Error updating: {toggleError.message ?? 'Unknown error'}
154
+ </p>
155
+ {/if}
156
+ {#if deleteError}
157
+ <p class="mt-4 text-red-500">
158
+ Error deleting: {deleteError.message ?? 'Unknown error'}
159
+ </p>
160
+ {/if}
161
+ </div>
162
+ {{else}}
163
+ <script lang="ts">
164
+ {{#if (eq api "orpc")}}
165
+ import { orpc } from '$lib/orpc';
166
+ {{/if}}
167
+ import { createQuery, createMutation } from '@tanstack/svelte-query';
168
+
169
+ let newTodoText = $state('');
170
+
171
+ {{#if (eq api "orpc")}}
172
+ const todosQuery = createQuery(orpc.todo.getAll.queryOptions());
173
+
174
+ const addMutation = createMutation(
175
+ orpc.todo.create.mutationOptions({
176
+ onSuccess: () => {
177
+ $todosQuery.refetch();
178
+ newTodoText = '';
179
+ },
180
+ onError: (error) => {
181
+ console.error('Failed to create todo:', error?.message ?? error);
182
+ },
183
+ })
184
+ );
185
+
186
+ const toggleMutation = createMutation(
187
+ orpc.todo.toggle.mutationOptions({
188
+ onSuccess: () => {
189
+ $todosQuery.refetch();
190
+ },
191
+ onError: (error) => {
192
+ console.error('Failed to toggle todo:', error?.message ?? error);
193
+ },
194
+ })
195
+ );
196
+
197
+ const deleteMutation = createMutation(
198
+ orpc.todo.delete.mutationOptions({
199
+ onSuccess: () => {
200
+ $todosQuery.refetch();
201
+ },
202
+ onError: (error) => {
203
+ console.error('Failed to delete todo:', error?.message ?? error);
204
+ },
205
+ })
206
+ );
207
+ {{/if}}
208
+
209
+ function handleAddTodo(event: SubmitEvent) {
210
+ event.preventDefault();
211
+ const text = newTodoText.trim();
212
+ if (text) {
213
+ $addMutation.mutate({ text });
214
+ }
215
+ }
216
+
217
+ function handleToggleTodo(id: number, completed: boolean) {
218
+ $toggleMutation.mutate({ id, completed: !completed });
219
+ }
220
+
221
+ function handleDeleteTodo(id: number) {
222
+ $deleteMutation.mutate({ id });
223
+ }
224
+
225
+ const isAdding = $derived($addMutation.isPending);
226
+ const canAdd = $derived(!isAdding && newTodoText.trim().length > 0);
227
+ const isLoadingTodos = $derived($todosQuery.isLoading);
228
+ const todos = $derived($todosQuery.data ?? []);
229
+ const hasTodos = $derived(todos.length > 0);
230
+
231
+ </script>
232
+
233
+ <div class="p-4">
234
+ <h1 class="text-xl mb-4">Todos{{#if (eq api "orpc")}} (oRPC){{/if}}</h1>
235
+
236
+ <form onsubmit={handleAddTodo} class="flex gap-2 mb-4">
237
+ <input
238
+ type="text"
239
+ bind:value={newTodoText}
240
+ placeholder="New task..."
241
+ disabled={isAdding}
242
+ class=" p-1 flex-grow"
243
+ />
244
+ <button
245
+ type="submit"
246
+ disabled={!canAdd}
247
+ class="bg-blue-500 text-white px-3 py-1 rounded disabled:opacity-50"
248
+ >
249
+ {#if isAdding}Adding...{:else}Add{/if}
250
+ </button>
251
+ </form>
252
+
253
+ {#if isLoadingTodos}
254
+ <p>Loading...</p>
255
+ {:else if !hasTodos}
256
+ <p>No todos yet.</p>
257
+ {:else}
258
+ <ul class="space-y-1">
259
+ {#each todos as todo (todo.id)}
260
+ {@const isToggling = $toggleMutation.isPending && $toggleMutation.variables?.id === todo.id}
261
+ {@const isDeleting = $deleteMutation.isPending && $deleteMutation.variables?.id === todo.id}
262
+ {@const isDisabled = isToggling || isDeleting}
263
+ <li
264
+ class="flex items-center justify-between p-2 "
265
+ class:opacity-50={isDisabled}
266
+ >
267
+ <div class="flex items-center gap-2">
268
+ <input
269
+ type="checkbox"
270
+ id={`todo-${todo.id}`}
271
+ checked={todo.completed}
272
+ onchange={() => handleToggleTodo(todo.id, todo.completed)}
273
+ disabled={isDisabled}
274
+ />
275
+ <label
276
+ for={`todo-${todo.id}`}
277
+ class:line-through={todo.completed}
278
+ >
279
+ {todo.text}
280
+ </label>
281
+ </div>
282
+ <button
283
+ type="button"
284
+ onclick={() => handleDeleteTodo(todo.id)}
285
+ disabled={isDisabled}
286
+ aria-label="Delete todo"
287
+ class="text-red-500 px-1 disabled:opacity-50"
288
+ >
289
+ {#if isDeleting}Deleting...{:else}X{/if}
290
+ </button>
291
+ </li>
292
+ {/each}
293
+ </ul>
294
+ {/if}
295
+
296
+ {#if $todosQuery.isError}
297
+ <p class="mt-4 text-red-500">
298
+ Error loading: {$todosQuery.error?.message ?? 'Unknown error'}
299
+ </p>
300
+ {/if}
301
+ {#if $addMutation.isError}
302
+ <p class="mt-4 text-red-500">
303
+ Error adding: {$addMutation.error?.message ?? 'Unknown error'}
304
+ </p>
305
+ {/if}
306
+ {#if $toggleMutation.isError}
307
+ <p class="mt-4 text-red-500">
308
+ Error updating: {$toggleMutation.error?.message ?? 'Unknown error'}
309
+ </p>
310
+ {/if}
311
+ {#if $deleteMutation.isError}
312
+ <p class="mt-4 text-red-500">
313
+ Error deleting: {$deleteMutation.error?.message ?? 'Unknown error'}
314
+ </p>
315
+ {/if}
316
+ </div>
317
+ {{/if}}
@@ -0,0 +1,5 @@
1
+ node-linker=hoisted
2
+ {{#if (includes frontend "nuxt")}}
3
+ shamefully-hoist=true
4
+ strict-peer-dependencies=false
5
+ {{/if}}
@@ -0,0 +1,3 @@
1
+ packages:
2
+ - "apps/*"
3
+ - "packages/*"
@@ -0,0 +1,25 @@
1
+ node_modules/
2
+ .expo/
3
+ dist/
4
+ npm-debug.*
5
+ *.jks
6
+ *.p8
7
+ *.p12
8
+ *.key
9
+ *.mobileprovision
10
+ *.orig.*
11
+ web-build/
12
+ # expo router
13
+ expo-env.d.ts
14
+
15
+ .env
16
+ .cache
17
+
18
+ ios
19
+ android
20
+
21
+ # macOS
22
+ .DS_Store
23
+
24
+ # Temporary files created by Metro to check the health of the file watcher
25
+ .metro-health-check*
@@ -0,0 +1,46 @@
1
+ import { TabBarIcon } from "@/components/tabbar-icon";
2
+ import { useColorScheme } from "@/lib/use-color-scheme";
3
+ import { Tabs } from "expo-router";
4
+
5
+ export default function TabLayout() {
6
+ const { isDarkColorScheme } = useColorScheme();
7
+
8
+ return (
9
+ <Tabs
10
+ screenOptions={{
11
+ headerShown: false,
12
+ tabBarActiveTintColor: isDarkColorScheme
13
+ ? "hsl(217.2 91.2% 59.8%)"
14
+ : "hsl(221.2 83.2% 53.3%)",
15
+ tabBarInactiveTintColor: isDarkColorScheme
16
+ ? "hsl(215 20.2% 65.1%)"
17
+ : "hsl(215.4 16.3% 46.9%)",
18
+ tabBarStyle: {
19
+ backgroundColor: isDarkColorScheme
20
+ ? "hsl(222.2 84% 4.9%)"
21
+ : "hsl(0 0% 100%)",
22
+ borderTopColor: isDarkColorScheme
23
+ ? "hsl(217.2 32.6% 17.5%)"
24
+ : "hsl(214.3 31.8% 91.4%)",
25
+ },
26
+ }}
27
+ >
28
+ <Tabs.Screen
29
+ name="index"
30
+ options={{
31
+ title: "Home",
32
+ tabBarIcon: ({ color }) => <TabBarIcon name="home" color={color} />,
33
+ }}
34
+ />
35
+ <Tabs.Screen
36
+ name="two"
37
+ options={{
38
+ title: "Explore",
39
+ tabBarIcon: ({ color }) => (
40
+ <TabBarIcon name="compass" color={color} />
41
+ ),
42
+ }}
43
+ />
44
+ </Tabs>
45
+ );
46
+ }
@@ -0,0 +1,19 @@
1
+ import { Container } from "@/components/container";
2
+ import { ScrollView, Text, View } from "react-native";
3
+
4
+ export default function TabOne() {
5
+ return (
6
+ <Container>
7
+ <ScrollView className="flex-1 p-6">
8
+ <View className="py-8">
9
+ <Text className="text-3xl font-bold text-foreground mb-2">
10
+ Tab One
11
+ </Text>
12
+ <Text className="text-lg text-muted-foreground">
13
+ Explore the first section of your app
14
+ </Text>
15
+ </View>
16
+ </ScrollView>
17
+ </Container>
18
+ );
19
+ }
@@ -0,0 +1,19 @@
1
+ import { Container } from "@/components/container";
2
+ import { ScrollView, Text, View } from "react-native";
3
+
4
+ export default function TabTwo() {
5
+ return (
6
+ <Container>
7
+ <ScrollView className="flex-1 p-6">
8
+ <View className="py-8">
9
+ <Text className="text-3xl font-bold text-foreground mb-2">
10
+ Tab Two
11
+ </Text>
12
+ <Text className="text-lg text-muted-foreground">
13
+ Discover more features and content
14
+ </Text>
15
+ </View>
16
+ </ScrollView>
17
+ </Container>
18
+ );
19
+ }
@@ -0,0 +1,67 @@
1
+ import { Ionicons, MaterialIcons } from "@expo/vector-icons";
2
+ import { Link } from "expo-router";
3
+ import { Drawer } from "expo-router/drawer";
4
+
5
+ import { HeaderButton } from "@/components/header-button";
6
+
7
+ const DrawerLayout = () => {
8
+ return (
9
+ <Drawer>
10
+ <Drawer.Screen
11
+ name="index"
12
+ options=\{{
13
+ headerTitle: "Home",
14
+ drawerLabel: "Home",
15
+ drawerIcon: ({ size, color }) => (
16
+ <Ionicons name="home-outline" size={size} color={color} />
17
+ ),
18
+ }}
19
+ />
20
+ <Drawer.Screen
21
+ name="(tabs)"
22
+ options=\{{
23
+ headerTitle: "Tabs",
24
+ drawerLabel: "Tabs",
25
+ drawerIcon: ({ size, color }) => (
26
+ <MaterialIcons name="border-bottom" size={size} color={color} />
27
+ ),
28
+ headerRight: () => (
29
+ <Link href="/modal" asChild>
30
+ <HeaderButton />
31
+ </Link>
32
+ ),
33
+ }}
34
+ />
35
+ {{#if (includes examples "todo")}}
36
+ <Drawer.Screen
37
+ name="todos"
38
+ options=\{{
39
+ headerTitle: "Todos",
40
+ drawerLabel: "Todos",
41
+ drawerIcon: ({ size, color }) => (
42
+ <Ionicons name="checkbox-outline" size={size} color={color} />
43
+ ),
44
+ }}
45
+ />
46
+ {{/if}}
47
+ {{#if (includes examples "ai")}}
48
+ <Drawer.Screen
49
+ name="ai"
50
+ options=\{{
51
+ headerTitle: "AI",
52
+ drawerLabel: "AI",
53
+ drawerIcon: ({ size, color }) => (
54
+ <Ionicons
55
+ name="chatbubble-ellipses-outline"
56
+ size={size}
57
+ color={color}
58
+ />
59
+ ),
60
+ }}
61
+ />
62
+ {{/if}}
63
+ </Drawer>
64
+ );
65
+ };
66
+
67
+ export default DrawerLayout;
@@ -0,0 +1,95 @@
1
+ import { View, Text, ScrollView } from "react-native";
2
+ import { Container } from "@/components/container";
3
+ {{#if (eq api "orpc")}}
4
+ import { useQuery } from "@tanstack/react-query";
5
+ import { orpc } from "@/utils/orpc";
6
+ {{/if}}
7
+ {{#if (eq api "trpc")}}
8
+ import { useQuery } from "@tanstack/react-query";
9
+ import { trpc } from "@/utils/trpc";
10
+ {{/if}}
11
+ {{#if (eq backend "convex")}}
12
+ import { useQuery } from "convex/react";
13
+ import { api } from "@{{ projectName }}/backend/convex/_generated/api";
14
+ {{/if}}
15
+
16
+ export default function Home() {
17
+ {{#if (eq api "orpc")}}
18
+ const healthCheck = useQuery(orpc.healthCheck.queryOptions());
19
+ {{/if}}
20
+ {{#if (eq api "trpc")}}
21
+ const healthCheck = useQuery(trpc.healthCheck.queryOptions());
22
+ {{/if}}
23
+ {{#if (eq backend "convex")}}
24
+ const healthCheck = useQuery(api.healthCheck.get);
25
+ {{/if}}
26
+
27
+ return (
28
+ <Container>
29
+ <ScrollView showsVerticalScrollIndicator={false} className="flex-1">
30
+ <Text className="font-mono text-foreground text-3xl font-bold mb-4">
31
+ TVI
32
+ </Text>
33
+ <View className="bg-card border border-border rounded-xl p-6 mb-6 shadow-sm">
34
+ {{#if (eq backend "convex")}}
35
+ <View className="flex-row items-center gap-3">
36
+ <View
37
+ className={`h-3 w-3 rounded-full ${
38
+ healthCheck ? "bg-green-500" : "bg-orange-500"
39
+ }`}
40
+ />
41
+ <View className="flex-1">
42
+ <Text className="text-sm font-medium text-card-foreground">
43
+ Convex
44
+ </Text>
45
+ <Text className="text-xs text-muted-foreground">
46
+ {healthCheck === undefined
47
+ ? "Checking connection..."
48
+ : healthCheck === "OK"
49
+ ? "All systems operational"
50
+ : "Service unavailable"}
51
+ </Text>
52
+ </View>
53
+ </View>
54
+ {{else}}
55
+ {{#unless (eq api "none")}}
56
+ <View className="flex-row items-center gap-3">
57
+ <View
58
+ className={`h-3 w-3 rounded-full ${
59
+ healthCheck.data ? "bg-green-500" : "bg-orange-500"
60
+ }`}
61
+ />
62
+ <View className="flex-1">
63
+ <Text className="text-sm font-medium text-card-foreground">
64
+ {{#if (eq api "orpc")}}
65
+ ORPC
66
+ {{/if}}
67
+ {{#if (eq api "trpc")}}
68
+ TRPC
69
+ {{/if}}
70
+ </Text>
71
+ <Text className="text-xs text-muted-foreground">
72
+ {{#if (eq api "orpc")}}
73
+ {healthCheck.isLoading
74
+ ? "Checking connection..."
75
+ : healthCheck.data
76
+ ? "All systems operational"
77
+ : "Service unavailable"}
78
+ {{/if}}
79
+ {{#if (eq api "trpc")}}
80
+ {healthCheck.isLoading
81
+ ? "Checking connection..."
82
+ : healthCheck.data
83
+ ? "All systems operational"
84
+ : "Service unavailable"}
85
+ {{/if}}
86
+ </Text>
87
+ </View>
88
+ </View>
89
+ {{/unless}}
90
+ {{/if}}
91
+ </View>
92
+ </ScrollView>
93
+ </Container>
94
+ );
95
+ }
@@ -0,0 +1,47 @@
1
+ import { ScrollViewStyleReset } from 'expo-router/html';
2
+ import { ReactNode } from 'react';
3
+
4
+ // This file is web-only and used to configure the root HTML for every
5
+ // web page during static rendering.
6
+ // The contents of this function only run in Node.js environments and
7
+ // do not have access to the DOM or browser APIs.
8
+ export default function Root({ children }: { children: ReactNode }) {
9
+ return (
10
+ <html lang="en">
11
+ <head>
12
+ <meta charSet="utf-8" />
13
+ <meta content="IE=edge" httpEquiv="X-UA-Compatible" />
14
+
15
+ {/*
16
+ This viewport disables scaling which makes the mobile website act more like a native app.
17
+ However this does reduce built-in accessibility. If you want to enable scaling, use this instead:
18
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
19
+ */}
20
+ <meta
21
+ content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1.00001,viewport-fit=cover"
22
+ name="viewport"
23
+ />
24
+ {/*
25
+ Disable body scrolling on web. This makes ScrollView components work closer to how they do on native.
26
+ However, body scrolling is often nice to have for mobile web. If you want to enable it, remove this line.
27
+ */}
28
+ <ScrollViewStyleReset />
29
+
30
+ {/* Using raw CSS styles as an escape-hatch to ensure the background color never flickers in dark-mode. */}
31
+ <style dangerouslySetInnerHTML={{ __html: responsiveBackground }} />
32
+ {/* Add any additional <head> elements that you want globally available on web... */}
33
+ </head>
34
+ <body>{children}</body>
35
+ </html>
36
+ );
37
+ }
38
+
39
+ const responsiveBackground = `
40
+ body {
41
+ background-color: #fff;
42
+ }
43
+ @media (prefers-color-scheme: dark) {
44
+ body {
45
+ background-color: #000;
46
+ }
47
+ }`;
@@ -0,0 +1,29 @@
1
+ import { Container } from "@/components/container";
2
+ import { Link, Stack } from "expo-router";
3
+ import { Text, View } from "react-native";
4
+
5
+ export default function NotFoundScreen() {
6
+ return (
7
+ <>
8
+ <Stack.Screen options={{ title: "Oops!" }} />
9
+ <Container>
10
+ <View className="flex-1 justify-center items-center p-6">
11
+ <View className="items-center">
12
+ <Text className="text-6xl mb-4">?</Text>
13
+ <Text className="text-2xl font-bold text-foreground mb-2 text-center">
14
+ Page Not Found
15
+ </Text>
16
+ <Text className="text-muted-foreground text-center mb-8 max-w-sm">
17
+ Sorry, the page you're looking for doesn't exist.
18
+ </Text>
19
+ <Link href="/" asChild>
20
+ <Text className="text-primary font-medium bg-primary/10 px-6 py-3 rounded-lg">
21
+ Go to Home
22
+ </Text>
23
+ </Link>
24
+ </View>
25
+ </View>
26
+ </Container>
27
+ </>
28
+ );
29
+ }