@cachly-dev/mcp-server 0.10.56 → 0.10.58

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 (338) hide show
  1. package/.github/workflows/eas-build-android.yml +58 -0
  2. package/.github/workflows/eas-update.yml +55 -0
  3. package/.github/workflows/publish.yml +2 -2
  4. package/BENCH.md +91 -0
  5. package/CHANGELOG.md +44 -0
  6. package/README.md +6 -34
  7. package/STRATEGY.md +228 -0
  8. package/apps/travel-chaos-organizer/.env.example +49 -0
  9. package/apps/travel-chaos-organizer/DEPLOY.md +328 -0
  10. package/apps/travel-chaos-organizer/Makefile +57 -0
  11. package/apps/travel-chaos-organizer/README.md +159 -0
  12. package/apps/travel-chaos-organizer/backend/.env.example +10 -0
  13. package/apps/travel-chaos-organizer/backend/Dockerfile +25 -0
  14. package/apps/travel-chaos-organizer/backend/alembic/env.py +56 -0
  15. package/apps/travel-chaos-organizer/backend/alembic/script.py.mako +24 -0
  16. package/apps/travel-chaos-organizer/backend/alembic/versions/.gitkeep +0 -0
  17. package/apps/travel-chaos-organizer/backend/alembic/versions/0001_stripe_customer_id.py +24 -0
  18. package/apps/travel-chaos-organizer/backend/alembic/versions/0002_telegram_chat_id.py +24 -0
  19. package/apps/travel-chaos-organizer/backend/alembic.ini +39 -0
  20. package/apps/travel-chaos-organizer/backend/app/__init__.py +0 -0
  21. package/apps/travel-chaos-organizer/backend/app/auth/__init__.py +0 -0
  22. package/apps/travel-chaos-organizer/backend/app/auth/keycloak.py +81 -0
  23. package/apps/travel-chaos-organizer/backend/app/config.py +66 -0
  24. package/apps/travel-chaos-organizer/backend/app/db/__init__.py +0 -0
  25. package/apps/travel-chaos-organizer/backend/app/db/database.py +20 -0
  26. package/apps/travel-chaos-organizer/backend/app/db/migrations/001_init.sql +71 -0
  27. package/apps/travel-chaos-organizer/backend/app/db/migrations/002_telemetry_freemium.sql +31 -0
  28. package/apps/travel-chaos-organizer/backend/app/db/migrations/003_telegram_chat_id.sql +4 -0
  29. package/apps/travel-chaos-organizer/backend/app/db/migrations/004_stripe_and_email.sql +5 -0
  30. package/apps/travel-chaos-organizer/backend/app/limiter.py +4 -0
  31. package/apps/travel-chaos-organizer/backend/app/logging_config.py +25 -0
  32. package/apps/travel-chaos-organizer/backend/app/main.py +77 -0
  33. package/apps/travel-chaos-organizer/backend/app/models/__init__.py +0 -0
  34. package/apps/travel-chaos-organizer/backend/app/models/schemas.py +142 -0
  35. package/apps/travel-chaos-organizer/backend/app/routers/__init__.py +0 -0
  36. package/apps/travel-chaos-organizer/backend/app/routers/admin.py +237 -0
  37. package/apps/travel-chaos-organizer/backend/app/routers/cache.py +13 -0
  38. package/apps/travel-chaos-organizer/backend/app/routers/events.py +29 -0
  39. package/apps/travel-chaos-organizer/backend/app/routers/export.py +122 -0
  40. package/apps/travel-chaos-organizer/backend/app/routers/inbox.py +122 -0
  41. package/apps/travel-chaos-organizer/backend/app/routers/items.py +130 -0
  42. package/apps/travel-chaos-organizer/backend/app/routers/mail.py +108 -0
  43. package/apps/travel-chaos-organizer/backend/app/routers/parse.py +303 -0
  44. package/apps/travel-chaos-organizer/backend/app/routers/payments.py +179 -0
  45. package/apps/travel-chaos-organizer/backend/app/routers/trips.py +134 -0
  46. package/apps/travel-chaos-organizer/backend/app/routers/users.py +169 -0
  47. package/apps/travel-chaos-organizer/backend/app/routers/waitlist.py +37 -0
  48. package/apps/travel-chaos-organizer/backend/app/services/__init__.py +0 -0
  49. package/apps/travel-chaos-organizer/backend/app/services/cache.py +131 -0
  50. package/apps/travel-chaos-organizer/backend/app/services/drip.py +128 -0
  51. package/apps/travel-chaos-organizer/backend/app/services/email.py +61 -0
  52. package/apps/travel-chaos-organizer/backend/app/services/errors.py +44 -0
  53. package/apps/travel-chaos-organizer/backend/app/services/notifier.py +10 -0
  54. package/apps/travel-chaos-organizer/backend/app/services/ollama.py +101 -0
  55. package/apps/travel-chaos-organizer/backend/app/services/parser.py +22 -0
  56. package/apps/travel-chaos-organizer/backend/app/services/quota.py +85 -0
  57. package/apps/travel-chaos-organizer/backend/app/services/sentry.py +11 -0
  58. package/apps/travel-chaos-organizer/backend/app/services/telemetry.py +35 -0
  59. package/apps/travel-chaos-organizer/backend/pytest.ini +4 -0
  60. package/apps/travel-chaos-organizer/backend/requirements-dev.txt +6 -0
  61. package/apps/travel-chaos-organizer/backend/requirements.txt +25 -0
  62. package/apps/travel-chaos-organizer/backend/tests/__init__.py +0 -0
  63. package/apps/travel-chaos-organizer/backend/tests/conftest.py +223 -0
  64. package/apps/travel-chaos-organizer/backend/tests/test_admin.py +99 -0
  65. package/apps/travel-chaos-organizer/backend/tests/test_cache.py +142 -0
  66. package/apps/travel-chaos-organizer/backend/tests/test_drip.py +32 -0
  67. package/apps/travel-chaos-organizer/backend/tests/test_e2e_chain.py +121 -0
  68. package/apps/travel-chaos-organizer/backend/tests/test_export.py +48 -0
  69. package/apps/travel-chaos-organizer/backend/tests/test_inbox.py +107 -0
  70. package/apps/travel-chaos-organizer/backend/tests/test_items.py +138 -0
  71. package/apps/travel-chaos-organizer/backend/tests/test_mail.py +113 -0
  72. package/apps/travel-chaos-organizer/backend/tests/test_ollama.py +145 -0
  73. package/apps/travel-chaos-organizer/backend/tests/test_parse_url.py +98 -0
  74. package/apps/travel-chaos-organizer/backend/tests/test_parser.py +72 -0
  75. package/apps/travel-chaos-organizer/backend/tests/test_payments.py +163 -0
  76. package/apps/travel-chaos-organizer/backend/tests/test_quota.py +46 -0
  77. package/apps/travel-chaos-organizer/backend/tests/test_search.py +67 -0
  78. package/apps/travel-chaos-organizer/backend/tests/test_trips.py +123 -0
  79. package/apps/travel-chaos-organizer/backend/tests/test_users.py +52 -0
  80. package/apps/travel-chaos-organizer/backups/.gitkeep +0 -0
  81. package/apps/travel-chaos-organizer/caddy/Caddyfile +27 -0
  82. package/apps/travel-chaos-organizer/docker-compose.node1.yml +31 -0
  83. package/apps/travel-chaos-organizer/docker-compose.override.yml +14 -0
  84. package/apps/travel-chaos-organizer/docker-compose.yml +128 -0
  85. package/apps/travel-chaos-organizer/frontend/.env.example +13 -0
  86. package/apps/travel-chaos-organizer/frontend/app/(app)/_layout.tsx +80 -0
  87. package/apps/travel-chaos-organizer/frontend/app/(app)/inbox.tsx +166 -0
  88. package/apps/travel-chaos-organizer/frontend/app/(app)/search.tsx +130 -0
  89. package/apps/travel-chaos-organizer/frontend/app/(app)/settings.tsx +381 -0
  90. package/apps/travel-chaos-organizer/frontend/app/(app)/trips/[id].tsx +346 -0
  91. package/apps/travel-chaos-organizer/frontend/app/(app)/trips.tsx +363 -0
  92. package/apps/travel-chaos-organizer/frontend/app/(app)/upgrade/cancel.tsx +34 -0
  93. package/apps/travel-chaos-organizer/frontend/app/(app)/upgrade/success.tsx +35 -0
  94. package/apps/travel-chaos-organizer/frontend/app/(auth)/login.tsx +91 -0
  95. package/apps/travel-chaos-organizer/frontend/app/_layout.tsx +71 -0
  96. package/apps/travel-chaos-organizer/frontend/app/auth.tsx +29 -0
  97. package/apps/travel-chaos-organizer/frontend/app/index.tsx +16 -0
  98. package/apps/travel-chaos-organizer/frontend/app/onboarding.tsx +119 -0
  99. package/apps/travel-chaos-organizer/frontend/app.config.ts +54 -0
  100. package/apps/travel-chaos-organizer/frontend/assets/adaptive-icon.svg +66 -0
  101. package/apps/travel-chaos-organizer/frontend/assets/icon.svg +79 -0
  102. package/apps/travel-chaos-organizer/frontend/assets/splash.svg +15 -0
  103. package/apps/travel-chaos-organizer/frontend/components/BottomSheet.tsx +102 -0
  104. package/apps/travel-chaos-organizer/frontend/components/ConfettiCannon.tsx +107 -0
  105. package/apps/travel-chaos-organizer/frontend/components/ConfirmDialog.tsx +109 -0
  106. package/apps/travel-chaos-organizer/frontend/components/ErrorBoundary.tsx +45 -0
  107. package/apps/travel-chaos-organizer/frontend/components/FileUploadButton.tsx +19 -0
  108. package/apps/travel-chaos-organizer/frontend/components/ImportSheet.tsx +155 -0
  109. package/apps/travel-chaos-organizer/frontend/components/OfflineBadge.tsx +55 -0
  110. package/apps/travel-chaos-organizer/frontend/components/QRCodeViewer.tsx +80 -0
  111. package/apps/travel-chaos-organizer/frontend/components/ScoreRing.tsx +39 -0
  112. package/apps/travel-chaos-organizer/frontend/components/Screen.tsx +39 -0
  113. package/apps/travel-chaos-organizer/frontend/components/Skeleton.tsx +70 -0
  114. package/apps/travel-chaos-organizer/frontend/components/SwipeableInboxItem.tsx +121 -0
  115. package/apps/travel-chaos-organizer/frontend/components/SwipeableTimelineItem.tsx +70 -0
  116. package/apps/travel-chaos-organizer/frontend/components/TimelineItem.tsx +73 -0
  117. package/apps/travel-chaos-organizer/frontend/components/TimelineItemDetail.tsx +130 -0
  118. package/apps/travel-chaos-organizer/frontend/components/Toast.tsx +60 -0
  119. package/apps/travel-chaos-organizer/frontend/components/ToastContext.tsx +42 -0
  120. package/apps/travel-chaos-organizer/frontend/components/TripCard.tsx +146 -0
  121. package/apps/travel-chaos-organizer/frontend/components/TripQRModal.tsx +70 -0
  122. package/apps/travel-chaos-organizer/frontend/components/UpgradeBanner.tsx +44 -0
  123. package/apps/travel-chaos-organizer/frontend/eas.json +22 -0
  124. package/apps/travel-chaos-organizer/frontend/hooks/useInbox.ts +24 -0
  125. package/apps/travel-chaos-organizer/frontend/hooks/useNetworkSync.ts +60 -0
  126. package/apps/travel-chaos-organizer/frontend/hooks/useTrips.ts +112 -0
  127. package/apps/travel-chaos-organizer/frontend/lib/api.ts +223 -0
  128. package/apps/travel-chaos-organizer/frontend/lib/auth.ts +93 -0
  129. package/apps/travel-chaos-organizer/frontend/lib/db.ts +86 -0
  130. package/apps/travel-chaos-organizer/frontend/lib/fileCache.ts +54 -0
  131. package/apps/travel-chaos-organizer/frontend/lib/haptics.ts +13 -0
  132. package/apps/travel-chaos-organizer/frontend/lib/notifications.ts +112 -0
  133. package/apps/travel-chaos-organizer/frontend/lib/offlineQueue.ts +145 -0
  134. package/apps/travel-chaos-organizer/frontend/lib/quota.tsx +43 -0
  135. package/apps/travel-chaos-organizer/frontend/lib/sentry.ts +28 -0
  136. package/apps/travel-chaos-organizer/frontend/lib/shareIntent.ts +44 -0
  137. package/apps/travel-chaos-organizer/frontend/package-lock.json +13221 -0
  138. package/apps/travel-chaos-organizer/frontend/package.json +44 -0
  139. package/apps/travel-chaos-organizer/frontend/tsconfig.json +23 -0
  140. package/apps/travel-chaos-organizer/keycloak/realm-export.json +59 -0
  141. package/apps/travel-chaos-organizer/landing/index.html +773 -0
  142. package/apps/travel-chaos-organizer/scripts/backup.sh +14 -0
  143. package/apps/travel-chaos-organizer/scripts/configure-env.sh +82 -0
  144. package/apps/travel-chaos-organizer/scripts/deploy.sh +54 -0
  145. package/apps/travel-chaos-organizer/scripts/health-check.sh +36 -0
  146. package/apps/travel-chaos-organizer/scripts/migrate.sh +7 -0
  147. package/apps/travel-chaos-organizer/scripts/server-setup.sh +85 -0
  148. package/apps/travel-chaos-organizer/scripts/smoke-test.sh +29 -0
  149. package/apps/travel-chaos-organizer/scripts/verify-tracking.sh +71 -0
  150. package/apps/travel-chaos-organizer/telegram-bot/.env.example +3 -0
  151. package/apps/travel-chaos-organizer/telegram-bot/Dockerfile +6 -0
  152. package/apps/travel-chaos-organizer/telegram-bot/bot.py +504 -0
  153. package/apps/travel-chaos-organizer/telegram-bot/requirements.txt +3 -0
  154. package/dist/packages/telegram-notify/client.d.ts +19 -0
  155. package/dist/packages/telegram-notify/client.js +75 -0
  156. package/dist/packages/telegram-notify/client.js.map +1 -0
  157. package/dist/src/__tests__/auth.test.js.map +1 -0
  158. package/dist/{__tests__ → src/__tests__}/brain-flow.test.js +141 -0
  159. package/dist/src/__tests__/brain-flow.test.js.map +1 -0
  160. package/dist/src/__tests__/confidence.test.js.map +1 -0
  161. package/dist/src/__tests__/e2e/api-auth.test.js.map +1 -0
  162. package/dist/src/__tests__/e2e/api-health.test.js.map +1 -0
  163. package/dist/src/__tests__/e2e/cli.test.js.map +1 -0
  164. package/dist/src/__tests__/e2e/config.js.map +1 -0
  165. package/dist/src/__tests__/e2e/funnel.test.js.map +1 -0
  166. package/dist/src/__tests__/e2e/mcp-tools.test.js.map +1 -0
  167. package/dist/src/__tests__/e2e/provisioning.test.js.map +1 -0
  168. package/dist/{__tests__ → src/__tests__}/handlers-unit.test.js +0 -2
  169. package/dist/src/__tests__/handlers-unit.test.js.map +1 -0
  170. package/dist/src/__tests__/namespace.test.js.map +1 -0
  171. package/dist/src/__tests__/rerank.test.js +115 -0
  172. package/dist/src/__tests__/rerank.test.js.map +1 -0
  173. package/dist/src/auth.js.map +1 -0
  174. package/dist/src/bench/cachly-bench.d.ts +35 -0
  175. package/dist/src/bench/cachly-bench.js +182 -0
  176. package/dist/src/bench/cachly-bench.js.map +1 -0
  177. package/dist/src/bench/fixtures.d.ts +37 -0
  178. package/dist/src/bench/fixtures.js +225 -0
  179. package/dist/src/bench/fixtures.js.map +1 -0
  180. package/dist/src/ckg.js.map +1 -0
  181. package/dist/src/confidence.js.map +1 -0
  182. package/dist/src/embeddings.js.map +1 -0
  183. package/dist/{handlers → src/handlers}/advanced.d.ts +1 -1
  184. package/dist/{handlers → src/handlers}/advanced.js +1 -2
  185. package/dist/src/handlers/advanced.js.map +1 -0
  186. package/dist/{handlers → src/handlers}/brain.js +361 -191
  187. package/dist/src/handlers/brain.js.map +1 -0
  188. package/dist/src/handlers/cache.js.map +1 -0
  189. package/dist/{handlers → src/handlers}/context.js +9 -4
  190. package/dist/src/handlers/context.js.map +1 -0
  191. package/dist/{handlers → src/handlers}/fedbrain.d.ts +6 -0
  192. package/dist/{handlers → src/handlers}/fedbrain.js +19 -7
  193. package/dist/src/handlers/fedbrain.js.map +1 -0
  194. package/dist/src/handlers/instances.js.map +1 -0
  195. package/dist/{handlers → src/handlers}/roadmap.d.ts +1 -1
  196. package/dist/{handlers → src/handlers}/roadmap.js +8 -4
  197. package/dist/src/handlers/roadmap.js.map +1 -0
  198. package/dist/src/handlers/syndicate.js.map +1 -0
  199. package/dist/src/handlers/tco.d.ts +16 -0
  200. package/dist/src/handlers/tco.js +303 -0
  201. package/dist/src/handlers/tco.js.map +1 -0
  202. package/dist/{handlers → src/handlers}/team.js +58 -7
  203. package/dist/src/handlers/team.js.map +1 -0
  204. package/dist/{index.js → src/index.js} +207 -429
  205. package/dist/src/index.js.map +1 -0
  206. package/dist/src/namespace.js.map +1 -0
  207. package/dist/src/notifier.d.ts +5 -0
  208. package/dist/src/notifier.js +6 -0
  209. package/dist/src/notifier.js.map +1 -0
  210. package/dist/src/rerank.d.ts +55 -0
  211. package/dist/src/rerank.js +111 -0
  212. package/dist/src/rerank.js.map +1 -0
  213. package/dist/{search.js → src/search.js} +2 -2
  214. package/dist/src/search.js.map +1 -0
  215. package/dist/src/search.test.d.ts +1 -0
  216. package/dist/{search.test.js → src/search.test.js} +1 -1
  217. package/dist/src/search.test.js.map +1 -0
  218. package/dist/{tools.d.ts → src/tools.d.ts} +226 -0
  219. package/dist/{tools.js → src/tools.js} +196 -0
  220. package/dist/src/tools.js.map +1 -0
  221. package/dist/src/utils.js.map +1 -0
  222. package/eslint.config.js +1 -1
  223. package/package.json +8 -7
  224. package/packages/telegram-notify/SPLIT_PLAN.md +106 -0
  225. package/packages/telegram-notify/client.ts +88 -0
  226. package/packages/telegram-notify/emojis.json +22 -0
  227. package/packages/telegram-notify/pyproject.toml +17 -0
  228. package/packages/telegram-notify/telegram_notify/__init__.py +3 -0
  229. package/packages/telegram-notify/telegram_notify/client.py +87 -0
  230. package/packages/telegram-notify/telegram_notify/emojis.json +22 -0
  231. package/scripts/generate-icons.js +28 -0
  232. package/server.json +3 -3
  233. package/smithery.yaml +27 -16
  234. package/src/__tests__/auth.test.ts +1 -1
  235. package/src/__tests__/brain-flow.test.ts +175 -1
  236. package/src/__tests__/e2e/api-health.test.ts +1 -1
  237. package/src/__tests__/handlers-unit.test.ts +1 -2
  238. package/src/__tests__/rerank.test.ts +130 -0
  239. package/src/bench/cachly-bench.ts +217 -0
  240. package/src/bench/fixtures.ts +256 -0
  241. package/src/handlers/advanced.ts +2 -6
  242. package/src/handlers/brain.ts +371 -194
  243. package/src/handlers/cache.ts +1 -2
  244. package/src/handlers/context.ts +10 -6
  245. package/src/handlers/fedbrain.ts +21 -12
  246. package/src/handlers/roadmap.ts +8 -4
  247. package/src/handlers/syndicate.ts +7 -4
  248. package/src/handlers/tco.ts +345 -0
  249. package/src/handlers/team.ts +70 -10
  250. package/src/index.ts +193 -419
  251. package/src/notifier.ts +5 -0
  252. package/src/rerank.ts +148 -0
  253. package/src/search.test.ts +1 -1
  254. package/src/search.ts +2 -2
  255. package/src/tools.ts +210 -0
  256. package/tsconfig.json +2 -3
  257. package/vitest.config.ts +2 -0
  258. package/dist/__tests__/auth.test.js.map +0 -1
  259. package/dist/__tests__/brain-flow.test.js.map +0 -1
  260. package/dist/__tests__/brain-predict-failures.test.d.ts +0 -17
  261. package/dist/__tests__/brain-predict-failures.test.js +0 -339
  262. package/dist/__tests__/brain-predict-failures.test.js.map +0 -1
  263. package/dist/__tests__/confidence.test.js.map +0 -1
  264. package/dist/__tests__/e2e/api-auth.test.js.map +0 -1
  265. package/dist/__tests__/e2e/api-health.test.js.map +0 -1
  266. package/dist/__tests__/e2e/cli.test.js.map +0 -1
  267. package/dist/__tests__/e2e/config.js.map +0 -1
  268. package/dist/__tests__/e2e/funnel.test.js.map +0 -1
  269. package/dist/__tests__/e2e/mcp-tools.test.js.map +0 -1
  270. package/dist/__tests__/e2e/provisioning.test.js.map +0 -1
  271. package/dist/__tests__/handlers-unit.test.js.map +0 -1
  272. package/dist/__tests__/namespace.test.js.map +0 -1
  273. package/dist/auth.js.map +0 -1
  274. package/dist/ckg.js.map +0 -1
  275. package/dist/confidence.js.map +0 -1
  276. package/dist/embeddings.js.map +0 -1
  277. package/dist/handlers/advanced.js.map +0 -1
  278. package/dist/handlers/brain.js.map +0 -1
  279. package/dist/handlers/cache.js.map +0 -1
  280. package/dist/handlers/context.js.map +0 -1
  281. package/dist/handlers/fedbrain.js.map +0 -1
  282. package/dist/handlers/instances.js.map +0 -1
  283. package/dist/handlers/roadmap.js.map +0 -1
  284. package/dist/handlers/syndicate.js.map +0 -1
  285. package/dist/handlers/team.js.map +0 -1
  286. package/dist/index.js.map +0 -1
  287. package/dist/namespace.js.map +0 -1
  288. package/dist/search.js.map +0 -1
  289. package/dist/search.test.js.map +0 -1
  290. package/dist/tools.js.map +0 -1
  291. package/dist/utils.js.map +0 -1
  292. package/src/__tests__/brain-predict-failures.test.ts +0 -430
  293. /package/dist/{__tests__ → src/__tests__}/auth.test.d.ts +0 -0
  294. /package/dist/{__tests__ → src/__tests__}/auth.test.js +0 -0
  295. /package/dist/{__tests__ → src/__tests__}/brain-flow.test.d.ts +0 -0
  296. /package/dist/{__tests__ → src/__tests__}/confidence.test.d.ts +0 -0
  297. /package/dist/{__tests__ → src/__tests__}/confidence.test.js +0 -0
  298. /package/dist/{__tests__ → src/__tests__}/e2e/api-auth.test.d.ts +0 -0
  299. /package/dist/{__tests__ → src/__tests__}/e2e/api-auth.test.js +0 -0
  300. /package/dist/{__tests__ → src/__tests__}/e2e/api-health.test.d.ts +0 -0
  301. /package/dist/{__tests__ → src/__tests__}/e2e/api-health.test.js +0 -0
  302. /package/dist/{__tests__ → src/__tests__}/e2e/cli.test.d.ts +0 -0
  303. /package/dist/{__tests__ → src/__tests__}/e2e/cli.test.js +0 -0
  304. /package/dist/{__tests__ → src/__tests__}/e2e/config.d.ts +0 -0
  305. /package/dist/{__tests__ → src/__tests__}/e2e/config.js +0 -0
  306. /package/dist/{__tests__ → src/__tests__}/e2e/funnel.test.d.ts +0 -0
  307. /package/dist/{__tests__ → src/__tests__}/e2e/funnel.test.js +0 -0
  308. /package/dist/{__tests__ → src/__tests__}/e2e/mcp-tools.test.d.ts +0 -0
  309. /package/dist/{__tests__ → src/__tests__}/e2e/mcp-tools.test.js +0 -0
  310. /package/dist/{__tests__ → src/__tests__}/e2e/provisioning.test.d.ts +0 -0
  311. /package/dist/{__tests__ → src/__tests__}/e2e/provisioning.test.js +0 -0
  312. /package/dist/{__tests__ → src/__tests__}/handlers-unit.test.d.ts +0 -0
  313. /package/dist/{__tests__ → src/__tests__}/namespace.test.d.ts +0 -0
  314. /package/dist/{__tests__ → src/__tests__}/namespace.test.js +0 -0
  315. /package/dist/{search.test.d.ts → src/__tests__/rerank.test.d.ts} +0 -0
  316. /package/dist/{auth.d.ts → src/auth.d.ts} +0 -0
  317. /package/dist/{auth.js → src/auth.js} +0 -0
  318. /package/dist/{ckg.d.ts → src/ckg.d.ts} +0 -0
  319. /package/dist/{ckg.js → src/ckg.js} +0 -0
  320. /package/dist/{confidence.d.ts → src/confidence.d.ts} +0 -0
  321. /package/dist/{confidence.js → src/confidence.js} +0 -0
  322. /package/dist/{embeddings.d.ts → src/embeddings.d.ts} +0 -0
  323. /package/dist/{embeddings.js → src/embeddings.js} +0 -0
  324. /package/dist/{handlers → src/handlers}/brain.d.ts +0 -0
  325. /package/dist/{handlers → src/handlers}/cache.d.ts +0 -0
  326. /package/dist/{handlers → src/handlers}/cache.js +0 -0
  327. /package/dist/{handlers → src/handlers}/context.d.ts +0 -0
  328. /package/dist/{handlers → src/handlers}/instances.d.ts +0 -0
  329. /package/dist/{handlers → src/handlers}/instances.js +0 -0
  330. /package/dist/{handlers → src/handlers}/syndicate.d.ts +0 -0
  331. /package/dist/{handlers → src/handlers}/syndicate.js +0 -0
  332. /package/dist/{handlers → src/handlers}/team.d.ts +0 -0
  333. /package/dist/{index.d.ts → src/index.d.ts} +0 -0
  334. /package/dist/{namespace.d.ts → src/namespace.d.ts} +0 -0
  335. /package/dist/{namespace.js → src/namespace.js} +0 -0
  336. /package/dist/{search.d.ts → src/search.d.ts} +0 -0
  337. /package/dist/{utils.d.ts → src/utils.d.ts} +0 -0
  338. /package/dist/{utils.js → src/utils.js} +0 -0
@@ -0,0 +1,119 @@
1
+ import { useRef, useState } from "react";
2
+ import {
3
+ Animated, Dimensions, FlatList, StyleSheet, Text,
4
+ TouchableOpacity, View,
5
+ } from "react-native";
6
+ import { useRouter } from "expo-router";
7
+ import AsyncStorage from "@react-native-async-storage/async-storage";
8
+ import { haptics } from "../lib/haptics";
9
+
10
+ const { width } = Dimensions.get("window");
11
+ const ONBOARDING_KEY = "tco_onboarding_done";
12
+
13
+ const SLIDES = [
14
+ {
15
+ icon: "✈️",
16
+ title: "Reisechaos ade",
17
+ body: "Alle Buchungen, Tickets und Dokumente an einem Ort — automatisch sortiert.",
18
+ },
19
+ {
20
+ icon: "📸",
21
+ title: "Einfach scannen",
22
+ body: "Screenshot machen, PDF teilen oder E-Mail einfügen. KI erkennt alle Details automatisch.",
23
+ },
24
+ {
25
+ icon: "📶",
26
+ title: "Offline verfügbar",
27
+ body: "Deine Tickets und QR-Codes sind auch ohne Internet abrufbar — am Gate, im Zug, überall.",
28
+ },
29
+ {
30
+ icon: "🔐",
31
+ title: "Deine Daten, dein Server",
32
+ body: "Alles läuft auf deiner eigenen Infrastruktur. Keine Cloud, kein Tracking.",
33
+ },
34
+ ];
35
+
36
+ export async function isOnboardingDone(): Promise<boolean> {
37
+ return (await AsyncStorage.getItem(ONBOARDING_KEY)) === "done";
38
+ }
39
+
40
+ export default function OnboardingScreen() {
41
+ const router = useRouter();
42
+ const [index, setIndex] = useState(0);
43
+ const scrollX = useRef(new Animated.Value(0)).current;
44
+ const listRef = useRef<FlatList>(null);
45
+
46
+ async function next() {
47
+ await haptics.tap();
48
+ if (index < SLIDES.length - 1) {
49
+ listRef.current?.scrollToIndex({ index: index + 1, animated: true });
50
+ setIndex(index + 1);
51
+ } else {
52
+ await finish();
53
+ }
54
+ }
55
+
56
+ async function finish() {
57
+ await AsyncStorage.setItem(ONBOARDING_KEY, "done");
58
+ await haptics.success();
59
+ router.replace("/(auth)/login");
60
+ }
61
+
62
+ return (
63
+ <View style={s.container}>
64
+ <TouchableOpacity style={s.skip} onPress={finish}>
65
+ <Text style={s.skipText}>Überspringen</Text>
66
+ </TouchableOpacity>
67
+
68
+ <Animated.FlatList
69
+ ref={listRef}
70
+ data={SLIDES}
71
+ horizontal
72
+ pagingEnabled
73
+ showsHorizontalScrollIndicator={false}
74
+ scrollEnabled={false}
75
+ onScroll={Animated.event([{ nativeEvent: { contentOffset: { x: scrollX } } }], { useNativeDriver: false })}
76
+ keyExtractor={(_, i) => String(i)}
77
+ renderItem={({ item }) => (
78
+ <View style={[s.slide, { width }]}>
79
+ <Text style={s.icon}>{item.icon}</Text>
80
+ <Text style={s.title}>{item.title}</Text>
81
+ <Text style={s.body}>{item.body}</Text>
82
+ </View>
83
+ )}
84
+ />
85
+
86
+ <View style={s.footer}>
87
+ <View style={s.dots}>
88
+ {SLIDES.map((_, i) => {
89
+ const inputRange = [(i - 1) * width, i * width, (i + 1) * width];
90
+ const dotWidth = scrollX.interpolate({ inputRange, outputRange: [8, 24, 8], extrapolate: "clamp" });
91
+ const opacity = scrollX.interpolate({ inputRange, outputRange: [0.3, 1, 0.3], extrapolate: "clamp" });
92
+ return <Animated.View key={i} style={[s.dot, { width: dotWidth, opacity }]} />;
93
+ })}
94
+ </View>
95
+
96
+ <TouchableOpacity style={s.button} onPress={next}>
97
+ <Text style={s.buttonText}>
98
+ {index < SLIDES.length - 1 ? "Weiter" : "Los geht's"}
99
+ </Text>
100
+ </TouchableOpacity>
101
+ </View>
102
+ </View>
103
+ );
104
+ }
105
+
106
+ const s = StyleSheet.create({
107
+ container: { flex: 1, backgroundColor: "#0f0f1a" },
108
+ skip: { position: "absolute", top: 56, right: 24, zIndex: 10 },
109
+ skipText: { color: "#4a4a7a", fontSize: 14 },
110
+ slide: { flex: 1, alignItems: "center", justifyContent: "center", padding: 40, gap: 24 },
111
+ icon: { fontSize: 88 },
112
+ title: { fontSize: 30, fontWeight: "800", color: "#fff", textAlign: "center", lineHeight: 36 },
113
+ body: { fontSize: 16, color: "#8888aa", textAlign: "center", lineHeight: 24, maxWidth: 300 },
114
+ footer: { padding: 32, gap: 24, alignItems: "center" },
115
+ dots: { flexDirection: "row", gap: 6, alignItems: "center" },
116
+ dot: { height: 8, borderRadius: 4, backgroundColor: "#4f46e5" },
117
+ button: { backgroundColor: "#4f46e5", paddingVertical: 16, borderRadius: 14, width: "100%", alignItems: "center" },
118
+ buttonText: { color: "#fff", fontSize: 17, fontWeight: "700" },
119
+ });
@@ -0,0 +1,54 @@
1
+ import { ExpoConfig } from "expo/config";
2
+
3
+ const config: ExpoConfig = {
4
+ name: "Travel Chaos Organizer",
5
+ slug: "travel-chaos-organizer",
6
+ version: "0.1.0",
7
+ scheme: "tco",
8
+ runtimeVersion: {
9
+ policy: "appVersion",
10
+ },
11
+ orientation: "portrait",
12
+ icon: "./assets/icon.png",
13
+ splash: {
14
+ image: "./assets/splash.png",
15
+ backgroundColor: "#0f0f1a",
16
+ },
17
+ updates: {
18
+ url: "https://u.expo.dev/ed92c1f4-4f25-411f-a945-3c77f58d8550",
19
+ enabled: true,
20
+ fallbackToCacheTimeout: 3000,
21
+ checkAutomatically: "ON_LOAD",
22
+ },
23
+ ios: {
24
+ bundleIdentifier: "dev.cachly.tco",
25
+ supportsTablet: true,
26
+ },
27
+ android: {
28
+ package: "dev.cachly.tco",
29
+ adaptiveIcon: {
30
+ foregroundImage: "./assets/adaptive-icon.png",
31
+ backgroundColor: "#0f0f1a",
32
+ },
33
+ },
34
+ plugins: [
35
+ "expo-router",
36
+ "expo-sqlite",
37
+ "expo-updates",
38
+ ["expo-image-picker", { photosPermission: "Allow TCO to access your photos for travel document scanning." }],
39
+ ["expo-document-picker", {}],
40
+ [
41
+ "expo-notifications",
42
+ {
43
+ icon: "./assets/icon.png",
44
+ color: "#4f46e5",
45
+ sounds: [],
46
+ },
47
+ ],
48
+ ],
49
+ extra: {
50
+ eas: { projectId: "ed92c1f4-4f25-411f-a945-3c77f58d8550" },
51
+ },
52
+ };
53
+
54
+ export default config;
@@ -0,0 +1,66 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" width="1024" height="1024">
2
+ <defs>
3
+ <radialGradient id="glow" cx="50%" cy="43%" r="27%" fx="50%" fy="43%">
4
+ <stop offset="0%" stop-color="#4f46e5" stop-opacity="0.25"/>
5
+ <stop offset="100%" stop-color="#4f46e5" stop-opacity="0"/>
6
+ </radialGradient>
7
+ <filter id="planeShadow" x="-20%" y="-20%" width="140%" height="140%">
8
+ <feDropShadow dx="0" dy="8" stdDeviation="16" flood-color="#4f46e5" flood-opacity="0.4"/>
9
+ </filter>
10
+ </defs>
11
+
12
+ <!-- Radial glow behind plane -->
13
+ <ellipse cx="512" cy="440" rx="300" ry="280" fill="url(#glow)"/>
14
+
15
+ <!-- Paper plane group, centered ~(512, 420) -->
16
+ <g filter="url(#planeShadow)">
17
+ <!-- Main wing body -->
18
+ <polygon
19
+ points="192,540 512,270 832,420 512,480"
20
+ fill="#ffffff"
21
+ opacity="0.97"
22
+ />
23
+ <!-- Lower wing / tail -->
24
+ <polygon
25
+ points="192,540 512,480 400,620"
26
+ fill="#c7c4f0"
27
+ opacity="0.85"
28
+ />
29
+ <!-- Nose crease highlight -->
30
+ <polygon
31
+ points="512,270 832,420 512,380"
32
+ fill="#e8e6ff"
33
+ opacity="0.6"
34
+ />
35
+ <!-- Tail fold accent -->
36
+ <polygon
37
+ points="400,620 512,480 512,560"
38
+ fill="#9d9ae8"
39
+ opacity="0.7"
40
+ />
41
+ </g>
42
+
43
+ <!-- TCO text -->
44
+ <text
45
+ x="512"
46
+ y="692"
47
+ font-family="system-ui, -apple-system, sans-serif"
48
+ font-size="140"
49
+ font-weight="700"
50
+ fill="#ffffff"
51
+ text-anchor="middle"
52
+ letter-spacing="12"
53
+ >TCO</text>
54
+
55
+ <!-- Subtitle text -->
56
+ <text
57
+ x="512"
58
+ y="778"
59
+ font-family="system-ui, -apple-system, sans-serif"
60
+ font-size="52"
61
+ font-weight="400"
62
+ fill="#6666aa"
63
+ text-anchor="middle"
64
+ letter-spacing="3"
65
+ >Travel Chaos Organizer</text>
66
+ </svg>
@@ -0,0 +1,79 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" width="1024" height="1024">
2
+ <defs>
3
+ <radialGradient id="glow" cx="50%" cy="43%" r="27%" fx="50%" fy="43%">
4
+ <stop offset="0%" stop-color="#4f46e5" stop-opacity="0.25"/>
5
+ <stop offset="100%" stop-color="#4f46e5" stop-opacity="0"/>
6
+ </radialGradient>
7
+ <radialGradient id="bgGlow" cx="50%" cy="50%" r="60%">
8
+ <stop offset="0%" stop-color="#1a1535"/>
9
+ <stop offset="100%" stop-color="#0f0f1a"/>
10
+ </radialGradient>
11
+ <filter id="planeShadow" x="-20%" y="-20%" width="140%" height="140%">
12
+ <feDropShadow dx="0" dy="8" stdDeviation="16" flood-color="#4f46e5" flood-opacity="0.4"/>
13
+ </filter>
14
+ </defs>
15
+
16
+ <!-- Background -->
17
+ <rect width="1024" height="1024" rx="220" fill="url(#bgGlow)"/>
18
+
19
+ <!-- Subtle top highlight -->
20
+ <rect width="1024" height="1024" rx="220" fill="url(#bgGlow)" opacity="0"/>
21
+
22
+ <!-- Accent border glow -->
23
+ <rect width="1024" height="1024" rx="220" fill="none" stroke="#4f46e5" stroke-width="2" stroke-opacity="0.15"/>
24
+
25
+ <!-- Radial glow behind plane -->
26
+ <ellipse cx="512" cy="440" rx="300" ry="280" fill="url(#glow)"/>
27
+
28
+ <!-- Paper plane group, centered ~(512, 420) -->
29
+ <g filter="url(#planeShadow)">
30
+ <!-- Main wing body: large right-pointing shape -->
31
+ <polygon
32
+ points="192,540 512,270 832,420 512,480"
33
+ fill="#ffffff"
34
+ opacity="0.97"
35
+ />
36
+ <!-- Lower wing / tail -->
37
+ <polygon
38
+ points="192,540 512,480 400,620"
39
+ fill="#c7c4f0"
40
+ opacity="0.85"
41
+ />
42
+ <!-- Nose crease highlight -->
43
+ <polygon
44
+ points="512,270 832,420 512,380"
45
+ fill="#e8e6ff"
46
+ opacity="0.6"
47
+ />
48
+ <!-- Tail fold accent -->
49
+ <polygon
50
+ points="400,620 512,480 512,560"
51
+ fill="#9d9ae8"
52
+ opacity="0.7"
53
+ />
54
+ </g>
55
+
56
+ <!-- TCO text -->
57
+ <text
58
+ x="512"
59
+ y="692"
60
+ font-family="system-ui, -apple-system, sans-serif"
61
+ font-size="140"
62
+ font-weight="700"
63
+ fill="#ffffff"
64
+ text-anchor="middle"
65
+ letter-spacing="12"
66
+ >TCO</text>
67
+
68
+ <!-- Subtitle text -->
69
+ <text
70
+ x="512"
71
+ y="778"
72
+ font-family="system-ui, -apple-system, sans-serif"
73
+ font-size="52"
74
+ font-weight="400"
75
+ fill="#6666aa"
76
+ text-anchor="middle"
77
+ letter-spacing="3"
78
+ >Travel Chaos Organizer</text>
79
+ </svg>
@@ -0,0 +1,15 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1284" height="2778" viewBox="0 0 1284 2778">
2
+ <rect width="1284" height="2778" fill="#0f0f1a"/>
3
+ <radialGradient id="g" cx="642" cy="1389" r="400" gradientUnits="userSpaceOnUse">
4
+ <stop offset="0" stop-color="#4f46e5" stop-opacity="0.25"/>
5
+ <stop offset="1" stop-color="#4f46e5" stop-opacity="0"/>
6
+ </radialGradient>
7
+ <circle cx="642" cy="1389" r="400" fill="url(#g)"/>
8
+ <g transform="translate(642,1330)">
9
+ <polygon points="-110,-80 160,0 -110,80 -40,0" fill="#ffffff"/>
10
+ <polygon points="-110,80 -40,0 -110,20" fill="#a5b4fc"/>
11
+ <polygon points="-110,-80 -40,0 20,-60" fill="#e0e7ff" opacity="0.6"/>
12
+ </g>
13
+ <text x="642" y="1510" font-family="system-ui,-apple-system,sans-serif" font-size="120" font-weight="700" fill="#ffffff" text-anchor="middle" letter-spacing="12">TCO</text>
14
+ <text x="642" y="1590" font-family="system-ui,-apple-system,sans-serif" font-size="40" fill="#6666aa" text-anchor="middle" letter-spacing="3">Travel Chaos Organizer</text>
15
+ </svg>
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Native-feel bottom sheet — replaces Modal for most action sheets.
3
+ * Animated slide-up, backdrop tap to dismiss, safe-area aware.
4
+ */
5
+ import { ReactNode, useEffect, useRef } from "react";
6
+ import {
7
+ Animated, Dimensions, KeyboardAvoidingView, Platform, Pressable, ScrollView, StyleSheet, View,
8
+ } from "react-native";
9
+ import { useSafeAreaInsets } from "react-native-safe-area-context";
10
+
11
+ const { height: SCREEN_HEIGHT } = Dimensions.get("window");
12
+
13
+ type Props = {
14
+ visible: boolean;
15
+ onClose: () => void;
16
+ children?: ReactNode;
17
+ /** Sheet takes up this fraction of screen height (default 0.5) */
18
+ heightFraction?: number;
19
+ /** Set true when sheet contains text inputs so keyboard doesn't cover them */
20
+ hasInputs?: boolean;
21
+ };
22
+
23
+ export default function BottomSheet({ visible, onClose, children, heightFraction = 0.5, hasInputs = false }: Props) {
24
+ const insets = useSafeAreaInsets();
25
+ const translateY = useRef(new Animated.Value(SCREEN_HEIGHT)).current;
26
+ const backdropOpacity = useRef(new Animated.Value(0)).current;
27
+
28
+ useEffect(() => {
29
+ if (visible) {
30
+ Animated.parallel([
31
+ Animated.spring(translateY, { toValue: 0, useNativeDriver: true, damping: 20, stiffness: 200 }),
32
+ Animated.timing(backdropOpacity, { toValue: 1, duration: 200, useNativeDriver: true }),
33
+ ]).start();
34
+ } else {
35
+ Animated.parallel([
36
+ Animated.timing(translateY, { toValue: SCREEN_HEIGHT, duration: 250, useNativeDriver: true }),
37
+ Animated.timing(backdropOpacity, { toValue: 0, duration: 200, useNativeDriver: true }),
38
+ ]).start();
39
+ }
40
+ }, [visible]);
41
+
42
+ if (!visible && (translateY as any).__getValue() === SCREEN_HEIGHT) return null;
43
+
44
+ return (
45
+ <View style={StyleSheet.absoluteFill} pointerEvents={visible ? "auto" : "none"}>
46
+ <Animated.View style={[s.backdrop, { opacity: backdropOpacity }]}>
47
+ <Pressable style={StyleSheet.absoluteFill} onPress={onClose} />
48
+ </Animated.View>
49
+
50
+ <Animated.View
51
+ style={[
52
+ s.sheet,
53
+ {
54
+ height: SCREEN_HEIGHT * heightFraction,
55
+ paddingBottom: insets.bottom + 16,
56
+ transform: [{ translateY }],
57
+ },
58
+ ]}
59
+ >
60
+ <View style={s.handle} />
61
+ {hasInputs ? (
62
+ <KeyboardAvoidingView
63
+ style={{ flex: 1 }}
64
+ behavior={Platform.OS === "ios" ? "padding" : "height"}
65
+ keyboardVerticalOffset={16}
66
+ >
67
+ <ScrollView
68
+ keyboardShouldPersistTaps="handled"
69
+ showsVerticalScrollIndicator={false}
70
+ contentContainerStyle={{ flexGrow: 1 }}
71
+ >
72
+ {children}
73
+ </ScrollView>
74
+ </KeyboardAvoidingView>
75
+ ) : children}
76
+ </Animated.View>
77
+ </View>
78
+ );
79
+ }
80
+
81
+ const s = StyleSheet.create({
82
+ backdrop: { ...StyleSheet.absoluteFillObject, backgroundColor: "#000000bb" },
83
+ sheet: {
84
+ position: "absolute",
85
+ bottom: 0,
86
+ left: 0,
87
+ right: 0,
88
+ backgroundColor: "#1a1a2e",
89
+ borderTopLeftRadius: 24,
90
+ borderTopRightRadius: 24,
91
+ paddingTop: 12,
92
+ paddingHorizontal: 20,
93
+ },
94
+ handle: {
95
+ width: 40,
96
+ height: 4,
97
+ borderRadius: 2,
98
+ backgroundColor: "#3a3a5e",
99
+ alignSelf: "center",
100
+ marginBottom: 20,
101
+ },
102
+ });
@@ -0,0 +1,107 @@
1
+ import { useEffect, useRef } from "react";
2
+ import { Animated, Dimensions, StyleSheet, View } from "react-native";
3
+
4
+ type Props = {
5
+ visible: boolean;
6
+ };
7
+
8
+ const COLORS = ["#4f46e5", "#a5b4fc", "#818cf8", "#c7d2fe", "#f59e0b", "#10b981"];
9
+ const COUNT = 24;
10
+ const { width } = Dimensions.get("window");
11
+
12
+ function rand(min: number, max: number) {
13
+ return Math.random() * (max - min) + min;
14
+ }
15
+
16
+ type Particle = {
17
+ x: Animated.Value;
18
+ y: Animated.Value;
19
+ opacity: Animated.Value;
20
+ scale: Animated.Value;
21
+ color: string;
22
+ startX: number;
23
+ };
24
+
25
+ export default function ConfettiCannon({ visible }: Props) {
26
+ const particles = useRef<Particle[]>(
27
+ Array.from({ length: COUNT }, (_, i) => ({
28
+ x: new Animated.Value(0),
29
+ y: new Animated.Value(0),
30
+ opacity: new Animated.Value(0),
31
+ scale: new Animated.Value(1),
32
+ color: COLORS[i % COLORS.length],
33
+ startX: rand(0, width),
34
+ }))
35
+ ).current;
36
+
37
+ useEffect(() => {
38
+ if (!visible) return;
39
+
40
+ particles.forEach((p) => {
41
+ p.x.setValue(0);
42
+ p.y.setValue(0);
43
+ p.opacity.setValue(1);
44
+ p.scale.setValue(1);
45
+ });
46
+
47
+ const animations = particles.map((p) => {
48
+ const duration = rand(900, 1200);
49
+ const tx = rand(-150, 150);
50
+ const ty = rand(-300, -50);
51
+
52
+ return Animated.parallel([
53
+ Animated.timing(p.x, { toValue: tx, duration, useNativeDriver: true }),
54
+ Animated.timing(p.y, { toValue: ty, duration, useNativeDriver: true }),
55
+ Animated.timing(p.scale, { toValue: 0.3, duration, useNativeDriver: true }),
56
+ Animated.sequence([
57
+ Animated.delay(duration * 0.6),
58
+ Animated.timing(p.opacity, { toValue: 0, duration: duration * 0.4, useNativeDriver: true }),
59
+ ]),
60
+ ]);
61
+ });
62
+
63
+ Animated.parallel(animations).start();
64
+ }, [visible]);
65
+
66
+ if (!visible) return null;
67
+
68
+ return (
69
+ <View style={s.container} pointerEvents="none">
70
+ {particles.map((p, i) => (
71
+ <Animated.View
72
+ key={i}
73
+ style={[
74
+ s.particle,
75
+ { backgroundColor: p.color, left: p.startX - 4 },
76
+ {
77
+ opacity: p.opacity,
78
+ transform: [
79
+ { translateX: p.x },
80
+ { translateY: p.y },
81
+ { scale: p.scale },
82
+ ],
83
+ },
84
+ ]}
85
+ />
86
+ ))}
87
+ </View>
88
+ );
89
+ }
90
+
91
+ const s = StyleSheet.create({
92
+ container: {
93
+ position: "absolute",
94
+ bottom: 80,
95
+ left: 0,
96
+ right: 0,
97
+ zIndex: 100,
98
+ pointerEvents: "none",
99
+ } as any,
100
+ particle: {
101
+ position: "absolute",
102
+ width: 10,
103
+ height: 10,
104
+ borderRadius: 5,
105
+ bottom: 0,
106
+ },
107
+ });
@@ -0,0 +1,109 @@
1
+ import React, { useEffect, useRef } from "react";
2
+ import { Animated, Pressable, StyleSheet, Text, TouchableOpacity, View } from "react-native";
3
+
4
+ type Action = {
5
+ label: string;
6
+ style?: "default" | "cancel" | "destructive";
7
+ onPress?: () => void;
8
+ };
9
+
10
+ type Props = {
11
+ visible: boolean;
12
+ title: string;
13
+ message?: string;
14
+ actions: Action[];
15
+ };
16
+
17
+ export default function ConfirmDialog({ visible, title, message, actions }: Props) {
18
+ const opacity = useRef(new Animated.Value(0)).current;
19
+ const scale = useRef(new Animated.Value(0.92)).current;
20
+
21
+ useEffect(() => {
22
+ if (visible) {
23
+ Animated.parallel([
24
+ Animated.timing(opacity, { toValue: 1, duration: 180, useNativeDriver: true }),
25
+ Animated.spring(scale, { toValue: 1, useNativeDriver: true, damping: 18, stiffness: 280 }),
26
+ ]).start();
27
+ } else {
28
+ Animated.parallel([
29
+ Animated.timing(opacity, { toValue: 0, duration: 140, useNativeDriver: true }),
30
+ Animated.timing(scale, { toValue: 0.92, duration: 140, useNativeDriver: true }),
31
+ ]).start();
32
+ }
33
+ }, [visible]);
34
+
35
+ if (!visible) return null;
36
+
37
+ return (
38
+ <View style={s.overlay} pointerEvents="box-none">
39
+ <Pressable style={StyleSheet.absoluteFill} />
40
+ <Animated.View style={[s.backdrop, { opacity }]} />
41
+ <Animated.View style={[s.card, { opacity, transform: [{ scale }] }]}>
42
+ <Text style={s.title}>{title}</Text>
43
+ {message ? <Text style={s.message}>{message}</Text> : null}
44
+ <View style={s.actions}>
45
+ {actions.map((a, i) => (
46
+ <TouchableOpacity
47
+ key={i}
48
+ style={[
49
+ s.btn,
50
+ a.style === "destructive" && s.btnDestructive,
51
+ a.style === "cancel" && s.btnCancel,
52
+ ]}
53
+ onPress={a.onPress}
54
+ activeOpacity={0.75}
55
+ accessibilityRole="button"
56
+ accessibilityLabel={a.label}
57
+ >
58
+ <Text
59
+ style={[
60
+ s.btnText,
61
+ a.style === "destructive" && s.btnTextDestructive,
62
+ a.style === "cancel" && s.btnTextCancel,
63
+ ]}
64
+ >
65
+ {a.label}
66
+ </Text>
67
+ </TouchableOpacity>
68
+ ))}
69
+ </View>
70
+ </Animated.View>
71
+ </View>
72
+ );
73
+ }
74
+
75
+ const s = StyleSheet.create({
76
+ overlay: {
77
+ ...StyleSheet.absoluteFillObject,
78
+ alignItems: "center",
79
+ justifyContent: "center",
80
+ zIndex: 9999,
81
+ },
82
+ backdrop: {
83
+ ...StyleSheet.absoluteFillObject,
84
+ backgroundColor: "#000000bb",
85
+ },
86
+ card: {
87
+ backgroundColor: "#1a1a2e",
88
+ borderRadius: 20,
89
+ padding: 24,
90
+ width: 300,
91
+ borderWidth: 1,
92
+ borderColor: "#2a2a4a",
93
+ gap: 8,
94
+ },
95
+ title: { color: "#fff", fontSize: 17, fontWeight: "700" },
96
+ message: { color: "#9999cc", fontSize: 14, lineHeight: 20 },
97
+ actions: { flexDirection: "column", gap: 8, marginTop: 8 },
98
+ btn: {
99
+ backgroundColor: "#4f46e5",
100
+ borderRadius: 12,
101
+ paddingVertical: 12,
102
+ alignItems: "center",
103
+ },
104
+ btnCancel: { backgroundColor: "transparent", borderWidth: 1, borderColor: "#2a2a4a" },
105
+ btnDestructive: { backgroundColor: "#7f1d1d" },
106
+ btnText: { color: "#fff", fontWeight: "700", fontSize: 15 },
107
+ btnTextCancel: { color: "#9999cc" },
108
+ btnTextDestructive: { color: "#fca5a5" },
109
+ });