wadi 1.0.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 (294) hide show
  1. package/.agent/workflows/auto_sync.md +19 -0
  2. package/.agent/workflows/kivo_pipeline.md +27 -0
  3. package/.devcontainer/devcontainer.json +27 -0
  4. package/.github/workflows/kivo-cicd.yml +45 -0
  5. package/.github/workflows/monorepo-ci.yml +51 -0
  6. package/.github/workflows/wadi-ci.yml +38 -0
  7. package/.husky/pre-commit +2 -0
  8. package/.prettierignore +12 -0
  9. package/.prettierrc +9 -0
  10. package/.vscode/settings.json +19 -0
  11. package/CHANGELOG.md +56 -0
  12. package/CODE_OF_CONDUCT.md +43 -0
  13. package/CONTRIBUTING.md +42 -0
  14. package/DEPLOY_GUIDE.md +143 -0
  15. package/GO_LIVE_CHECKLIST.md +41 -0
  16. package/GO_LIVE_COMPLETE_REPORT.md +58 -0
  17. package/GO_LIVE_VALIDATION.md +39 -0
  18. package/LEGAL.md +38 -0
  19. package/MANIFESTO.md +40 -0
  20. package/MANUAL.md +82 -0
  21. package/OPS_PLAN.md +90 -0
  22. package/README.md +126 -0
  23. package/RELEASE_NOTES.md +51 -0
  24. package/ROADMAP.md +51 -0
  25. package/api_listado.txt +2197 -0
  26. package/apps/api/WADI_PROTOCOL.md +52 -0
  27. package/apps/api/debug-brain.js +11 -0
  28. package/apps/api/package.json +21 -0
  29. package/apps/api/src/core/analisis.js +17 -0
  30. package/apps/api/src/core/errors.js +31 -0
  31. package/apps/api/src/core/logger.js +32 -0
  32. package/apps/api/src/core/prompt-kivo.js +62 -0
  33. package/apps/api/src/index.js +212 -0
  34. package/apps/api/src/layers/human_pattern/composeResponse.js +35 -0
  35. package/apps/api/src/layers/human_pattern/detectPattern.js +39 -0
  36. package/apps/api/src/layers/human_pattern/heuristics.js +28 -0
  37. package/apps/api/src/layers/human_pattern/index.js +28 -0
  38. package/apps/api/src/layers/human_pattern/socialMemory.js +35 -0
  39. package/apps/api/src/middleware/errorHandler.js +24 -0
  40. package/apps/api/src/middleware/rateLimiter.js +38 -0
  41. package/apps/api/src/middleware/requestLogger.js +31 -0
  42. package/apps/api/src/middleware/upload.js +21 -0
  43. package/apps/api/src/middleware/validation.js +70 -0
  44. package/apps/api/src/modules/data.js +25 -0
  45. package/apps/api/src/modules/marketing.js +54 -0
  46. package/apps/api/src/modules/projects.js +40 -0
  47. package/apps/api/src/openai.js +16 -0
  48. package/apps/api/src/preferences/index.js +20 -0
  49. package/apps/api/src/register_user.js +22 -0
  50. package/apps/api/src/routes/kivo.js +58 -0
  51. package/apps/api/src/routes/monitoring.js +55 -0
  52. package/apps/api/src/routes.js +656 -0
  53. package/apps/api/src/supabase.js +17 -0
  54. package/apps/api/src/tools/index.js +57 -0
  55. package/apps/api/src/wadi-brain.js +171 -0
  56. package/apps/api/supabase/migrations/20251218_audit_logs.sql +27 -0
  57. package/apps/api/supabase/migrations/v2-chat-persistence.sql +83 -0
  58. package/apps/api/supabase/migrations/v3-cascade-delete.sql +11 -0
  59. package/apps/api/supabase/migrations/v3-security-fix.sql +90 -0
  60. package/apps/api/supabase/migrations/v3-storage.sql +36 -0
  61. package/apps/api/supabase/migrations/v4-gamification.sql +83 -0
  62. package/apps/api/supabase/migrations/v5-smoke-index.sql +6 -0
  63. package/apps/api/supabase/migrations/v6-schema-integrity-fix.sql +98 -0
  64. package/apps/api/supabase/migrations/v7-performance-indexes.sql +5 -0
  65. package/apps/api/supabase/migrations/v7-security-hardening.sql +132 -0
  66. package/apps/api/supabase/migrations/v8-security-hardening.sql +79 -0
  67. package/apps/api/test_human_pattern.js +30 -0
  68. package/apps/api/test_human_pattern_vague.js +30 -0
  69. package/apps/api/test_output.txt +76 -0
  70. package/apps/api/test_vague.js +30 -0
  71. package/apps/api/test_vague_block.js +30 -0
  72. package/apps/api/test_wadi.js +31 -0
  73. package/apps/api/tsconfig.json +13 -0
  74. package/apps/frontend/.env.local +3 -0
  75. package/apps/frontend/README.md +73 -0
  76. package/apps/frontend/eslint.config.js +27 -0
  77. package/apps/frontend/index.html +49 -0
  78. package/apps/frontend/package.json +41 -0
  79. package/apps/frontend/postcss.config.cjs +6 -0
  80. package/apps/frontend/public/cursors/wadi-neutral.svg +1 -0
  81. package/apps/frontend/public/cursors/wadi-select.svg +1 -0
  82. package/apps/frontend/public/icon-192.svg +1 -0
  83. package/apps/frontend/public/icon-512.svg +1 -0
  84. package/apps/frontend/public/manifest.webmanifest +23 -0
  85. package/apps/frontend/public/sw.js +57 -0
  86. package/apps/frontend/public/vite.svg +1 -0
  87. package/apps/frontend/public/wadi.svg +5 -0
  88. package/apps/frontend/src/assets/react.svg +1 -0
  89. package/apps/frontend/src/components/AuthLoader.tsx +50 -0
  90. package/apps/frontend/src/components/ChatInput.tsx +272 -0
  91. package/apps/frontend/src/components/ChatInterface.tsx +202 -0
  92. package/apps/frontend/src/components/ErrorBoundary.tsx +52 -0
  93. package/apps/frontend/src/components/InputArea.tsx +201 -0
  94. package/apps/frontend/src/components/Layout.tsx +73 -0
  95. package/apps/frontend/src/components/MessageBubble.tsx +66 -0
  96. package/apps/frontend/src/components/OnboardingModal.tsx +108 -0
  97. package/apps/frontend/src/components/SettingsModal.tsx +187 -0
  98. package/apps/frontend/src/components/Sidebar.tsx +171 -0
  99. package/apps/frontend/src/components/WadiOnboarding.tsx +71 -0
  100. package/apps/frontend/src/components/auditor/AuditReport.tsx +166 -0
  101. package/apps/frontend/src/components/auditor/AuditorHeader.tsx +34 -0
  102. package/apps/frontend/src/components/auditor/ContextPanel.tsx +138 -0
  103. package/apps/frontend/src/components/auditor/DataDeconstructor.tsx +85 -0
  104. package/apps/frontend/src/components/auditor/DecisionWall.tsx +65 -0
  105. package/apps/frontend/src/components/auditor/Dropzone.tsx +137 -0
  106. package/apps/frontend/src/components/common/Button.tsx +97 -0
  107. package/apps/frontend/src/components/common/Card.tsx +43 -0
  108. package/apps/frontend/src/components/common/Input.tsx +73 -0
  109. package/apps/frontend/src/components/common/Modal.tsx +53 -0
  110. package/apps/frontend/src/components/ui/Button.tsx +68 -0
  111. package/apps/frontend/src/components/ui/Card.tsx +86 -0
  112. package/apps/frontend/src/components/ui/Input.tsx +28 -0
  113. package/apps/frontend/src/components/ui/LogItem.tsx +64 -0
  114. package/apps/frontend/src/components/ui/MondayButton.tsx +40 -0
  115. package/apps/frontend/src/components/ui/MondayCard.tsx +24 -0
  116. package/apps/frontend/src/components/ui/Scouter.tsx +208 -0
  117. package/apps/frontend/src/components/ui/TerminalInput.tsx +202 -0
  118. package/apps/frontend/src/components/ui/Tooltip.tsx +67 -0
  119. package/apps/frontend/src/config/chatShortcuts.ts +20 -0
  120. package/apps/frontend/src/config/supabase.ts +6 -0
  121. package/apps/frontend/src/final_status.txt +3 -0
  122. package/apps/frontend/src/hooks/useScouter.ts +28 -0
  123. package/apps/frontend/src/hooks/useStoreHydration.ts +24 -0
  124. package/apps/frontend/src/improvement_status.txt +5 -0
  125. package/apps/frontend/src/index.css +88 -0
  126. package/apps/frontend/src/main.tsx +62 -0
  127. package/apps/frontend/src/monday_status.txt +7 -0
  128. package/apps/frontend/src/pages/ChatPage.tsx +201 -0
  129. package/apps/frontend/src/pages/DashboardPage.tsx +375 -0
  130. package/apps/frontend/src/pages/IntroWadi.tsx +114 -0
  131. package/apps/frontend/src/pages/LandingPage.tsx +103 -0
  132. package/apps/frontend/src/pages/Login.tsx +190 -0
  133. package/apps/frontend/src/pages/PrivacyPage.tsx +213 -0
  134. package/apps/frontend/src/pages/ProjectDetail.tsx +80 -0
  135. package/apps/frontend/src/pages/Projects.tsx +247 -0
  136. package/apps/frontend/src/pages/TermsPage.tsx +202 -0
  137. package/apps/frontend/src/router.tsx +83 -0
  138. package/apps/frontend/src/store/authStore.ts +152 -0
  139. package/apps/frontend/src/store/chatStore.ts +837 -0
  140. package/apps/frontend/src/store/documentStore.ts +89 -0
  141. package/apps/frontend/src/store/projectsStore.ts +111 -0
  142. package/apps/frontend/src/store/runsStore.ts +98 -0
  143. package/apps/frontend/src/utils/api.ts +34 -0
  144. package/apps/frontend/src/vite-env.d.ts +7 -0
  145. package/apps/frontend/tailwind.config.cjs +32 -0
  146. package/apps/frontend/tsconfig.app.json +27 -0
  147. package/apps/frontend/tsconfig.json +7 -0
  148. package/apps/frontend/tsconfig.node.json +26 -0
  149. package/apps/frontend/vite.config.ts +25 -0
  150. package/apps/kivo/.firebase/hosting.d3d3.cache +11 -0
  151. package/apps/kivo/.firebaserc +5 -0
  152. package/apps/kivo/BRANDING_GUIDE.md +71 -0
  153. package/apps/kivo/DEPLOYMENT_READY.md +46 -0
  154. package/apps/kivo/DEPLOY_URL.md +12 -0
  155. package/apps/kivo/IMPLEMENTATION_REPORT.md +35 -0
  156. package/apps/kivo/IMPLEMENTATION_REPORT_FINAL.md +49 -0
  157. package/apps/kivo/PLAN_MOBILE_2.0.md +44 -0
  158. package/apps/kivo/PWA_VERIFICATION_GUIDE.md +77 -0
  159. package/apps/kivo/README.md +28 -0
  160. package/apps/kivo/REBUILD_REPORT.md +34 -0
  161. package/apps/kivo/UPGRADE_REPORT.md +35 -0
  162. package/apps/kivo/android/app/build.gradle +54 -0
  163. package/apps/kivo/android/app/capacitor.build.gradle +19 -0
  164. package/apps/kivo/android/app/proguard-rules.pro +21 -0
  165. package/apps/kivo/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java +26 -0
  166. package/apps/kivo/android/app/src/main/AndroidManifest.xml +35 -0
  167. package/apps/kivo/android/app/src/main/java/com/kivo/app/MainActivity.java +5 -0
  168. package/apps/kivo/android/app/src/main/res/drawable/ic_launcher_background.xml +170 -0
  169. package/apps/kivo/android/app/src/main/res/drawable/splash.png +0 -0
  170. package/apps/kivo/android/app/src/main/res/drawable-land-hdpi/splash.png +0 -0
  171. package/apps/kivo/android/app/src/main/res/drawable-land-ldpi/splash.png +0 -0
  172. package/apps/kivo/android/app/src/main/res/drawable-land-mdpi/splash.png +0 -0
  173. package/apps/kivo/android/app/src/main/res/drawable-land-night-hdpi/splash.png +0 -0
  174. package/apps/kivo/android/app/src/main/res/drawable-land-night-ldpi/splash.png +0 -0
  175. package/apps/kivo/android/app/src/main/res/drawable-land-night-mdpi/splash.png +0 -0
  176. package/apps/kivo/android/app/src/main/res/drawable-land-night-xhdpi/splash.png +0 -0
  177. package/apps/kivo/android/app/src/main/res/drawable-land-night-xxhdpi/splash.png +0 -0
  178. package/apps/kivo/android/app/src/main/res/drawable-land-night-xxxhdpi/splash.png +0 -0
  179. package/apps/kivo/android/app/src/main/res/drawable-land-xhdpi/splash.png +0 -0
  180. package/apps/kivo/android/app/src/main/res/drawable-land-xxhdpi/splash.png +0 -0
  181. package/apps/kivo/android/app/src/main/res/drawable-land-xxxhdpi/splash.png +0 -0
  182. package/apps/kivo/android/app/src/main/res/drawable-night/splash.png +0 -0
  183. package/apps/kivo/android/app/src/main/res/drawable-port-hdpi/splash.png +0 -0
  184. package/apps/kivo/android/app/src/main/res/drawable-port-ldpi/splash.png +0 -0
  185. package/apps/kivo/android/app/src/main/res/drawable-port-mdpi/splash.png +0 -0
  186. package/apps/kivo/android/app/src/main/res/drawable-port-night-hdpi/splash.png +0 -0
  187. package/apps/kivo/android/app/src/main/res/drawable-port-night-ldpi/splash.png +0 -0
  188. package/apps/kivo/android/app/src/main/res/drawable-port-night-mdpi/splash.png +0 -0
  189. package/apps/kivo/android/app/src/main/res/drawable-port-night-xhdpi/splash.png +0 -0
  190. package/apps/kivo/android/app/src/main/res/drawable-port-night-xxhdpi/splash.png +0 -0
  191. package/apps/kivo/android/app/src/main/res/drawable-port-night-xxxhdpi/splash.png +0 -0
  192. package/apps/kivo/android/app/src/main/res/drawable-port-xhdpi/splash.png +0 -0
  193. package/apps/kivo/android/app/src/main/res/drawable-port-xxhdpi/splash.png +0 -0
  194. package/apps/kivo/android/app/src/main/res/drawable-port-xxxhdpi/splash.png +0 -0
  195. package/apps/kivo/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +34 -0
  196. package/apps/kivo/android/app/src/main/res/layout/activity_main.xml +12 -0
  197. package/apps/kivo/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +9 -0
  198. package/apps/kivo/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +9 -0
  199. package/apps/kivo/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  200. package/apps/kivo/android/app/src/main/res/mipmap-hdpi/ic_launcher_background.png +0 -0
  201. package/apps/kivo/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png +0 -0
  202. package/apps/kivo/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
  203. package/apps/kivo/android/app/src/main/res/mipmap-ldpi/ic_launcher.png +0 -0
  204. package/apps/kivo/android/app/src/main/res/mipmap-ldpi/ic_launcher_background.png +0 -0
  205. package/apps/kivo/android/app/src/main/res/mipmap-ldpi/ic_launcher_foreground.png +0 -0
  206. package/apps/kivo/android/app/src/main/res/mipmap-ldpi/ic_launcher_round.png +0 -0
  207. package/apps/kivo/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  208. package/apps/kivo/android/app/src/main/res/mipmap-mdpi/ic_launcher_background.png +0 -0
  209. package/apps/kivo/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png +0 -0
  210. package/apps/kivo/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
  211. package/apps/kivo/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  212. package/apps/kivo/android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png +0 -0
  213. package/apps/kivo/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png +0 -0
  214. package/apps/kivo/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
  215. package/apps/kivo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  216. package/apps/kivo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png +0 -0
  217. package/apps/kivo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png +0 -0
  218. package/apps/kivo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
  219. package/apps/kivo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  220. package/apps/kivo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png +0 -0
  221. package/apps/kivo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png +0 -0
  222. package/apps/kivo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
  223. package/apps/kivo/android/app/src/main/res/values/ic_launcher_background.xml +4 -0
  224. package/apps/kivo/android/app/src/main/res/values/strings.xml +7 -0
  225. package/apps/kivo/android/app/src/main/res/values/styles.xml +22 -0
  226. package/apps/kivo/android/app/src/main/res/xml/file_paths.xml +5 -0
  227. package/apps/kivo/android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java +18 -0
  228. package/apps/kivo/android/build.gradle +29 -0
  229. package/apps/kivo/android/capacitor.settings.gradle +3 -0
  230. package/apps/kivo/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  231. package/apps/kivo/android/gradle/wrapper/gradle-wrapper.properties +7 -0
  232. package/apps/kivo/android/gradle.properties +22 -0
  233. package/apps/kivo/android/gradlew +252 -0
  234. package/apps/kivo/android/gradlew.bat +94 -0
  235. package/apps/kivo/android/settings.gradle +5 -0
  236. package/apps/kivo/android/variables.gradle +16 -0
  237. package/apps/kivo/assets/icon.png +0 -0
  238. package/apps/kivo/assets/splash.png +0 -0
  239. package/apps/kivo/capacitor.config.json +16 -0
  240. package/apps/kivo/firebase.json +6 -0
  241. package/apps/kivo/jest.config.js +4 -0
  242. package/apps/kivo/package.json +26 -0
  243. package/apps/kivo/tests_disabled/logic.test.js +34 -0
  244. package/apps/kivo/www/assets/icon-192.png +0 -0
  245. package/apps/kivo/www/assets/icon-512.png +0 -0
  246. package/apps/kivo/www/assets/kivo-icon.png +0 -0
  247. package/apps/kivo/www/assets/pop.mp3 +0 -0
  248. package/apps/kivo/www/favicon.ico +0 -0
  249. package/apps/kivo/www/firebase-config.js +75 -0
  250. package/apps/kivo/www/index.html +38 -0
  251. package/apps/kivo/www/manifest.json +29 -0
  252. package/apps/kivo/www/script.js +72 -0
  253. package/apps/kivo/www/style.css +82 -0
  254. package/apps/kivo/www/sw.js +46 -0
  255. package/apps/kivo-brain-api/.env.example +2 -0
  256. package/apps/kivo-brain-api/KIVO_BACKEND_SETUP.md +27 -0
  257. package/apps/kivo-brain-api/controllers/kivoController.js +31 -0
  258. package/apps/kivo-brain-api/index.js +24 -0
  259. package/apps/kivo-brain-api/package.json +17 -0
  260. package/apps/kivo-brain-api/routes/message.js +8 -0
  261. package/apps/kivo-brain-api/services/openaiService.js +64 -0
  262. package/apps/tests/wadi-tests.js +155 -0
  263. package/apps/wadi-brain/docs/README_REMOVE_OPTIONS.md +26 -0
  264. package/cli/commands/deploy.js +10 -0
  265. package/cli/commands/docs.js +16 -0
  266. package/cli/commands/explain.js +29 -0
  267. package/cli/commands/lint.js +14 -0
  268. package/cli/index.js +26 -0
  269. package/cli/package.json +12 -0
  270. package/docs/CNAME +1 -0
  271. package/docs/README.md +38 -0
  272. package/docs/USO.md +30 -0
  273. package/docs/index.html +46 -0
  274. package/eslint.config.js +101 -0
  275. package/final_validation.json +27 -0
  276. package/frontend_listado.txt +33387 -0
  277. package/listado.txt +12591 -0
  278. package/package.json +46 -0
  279. package/packages/logger/index.js +43 -0
  280. package/packages/logger/package.json +19 -0
  281. package/packages/logger/test-logger.js +7 -0
  282. package/packages_listado.txt +801 -0
  283. package/pnpm-workspace.yaml +6 -0
  284. package/reseteador_existencial.ps1 +40 -0
  285. package/scripts/bump-version.js +26 -0
  286. package/scripts/env.ps1 +13 -0
  287. package/scripts/setup_android.ps1 +25 -0
  288. package/scripts/setup_virtual_env.ps1 +77 -0
  289. package/scripts/smoke-test.js +84 -0
  290. package/scripts/trigger_render_deploy.ps1 +3 -0
  291. package/scripts/validate-release.js +34 -0
  292. package/temp_check.js +57 -0
  293. package/tsconfig.json +5 -0
  294. package/validation_report.json +27 -0
@@ -0,0 +1,52 @@
1
+ import { Component, type ErrorInfo, type ReactNode } from "react";
2
+
3
+ type Props = {
4
+ children: ReactNode;
5
+ };
6
+
7
+ type State = {
8
+ hasError: boolean;
9
+ error?: Error;
10
+ };
11
+
12
+ export class ErrorBoundary extends Component<Props, State> {
13
+ state: State = { hasError: false };
14
+
15
+ static getDerivedStateFromError(error: Error): State {
16
+ return { hasError: true, error };
17
+ }
18
+
19
+ componentDidCatch(error: Error, errorInfo: ErrorInfo) {
20
+ console.error("Uncaught error:", error, errorInfo);
21
+ }
22
+
23
+ render() {
24
+ if (this.state.hasError) {
25
+ return (
26
+ <div className="flex flex-col items-center justify-center min-h-screen bg-[var(--wadi-bg)] text-[var(--wadi-alert)] p-8 text-center font-mono-wadi">
27
+ <h1 className="text-4xl font-bold mb-4">
28
+ 💥 CRITICAL SYSTEM FAILURE
29
+ </h1>
30
+ <p className="text-xl text-[var(--wadi-text)] mb-8">
31
+ Ni siquiera WADI puede fingir que esto está bien.
32
+ </p>
33
+ <div className="bg-black/50 p-4 rounded border border-[var(--wadi-alert)]/30 text-left max-w-2xl overflow-auto text-xs text-red-300">
34
+ <p className="font-bold mb-2">ERROR TRACE:</p>
35
+ <pre>{this.state.error?.message}</pre>
36
+ <pre className="mt-2 text-[var(--wadi-text-muted)] opacity-50">
37
+ {this.state.error?.stack}
38
+ </pre>
39
+ </div>
40
+ <button
41
+ onClick={() => window.location.reload()}
42
+ className="mt-8 px-6 py-2 bg-[var(--wadi-alert)] text-white rounded hover:bg-red-600 transition-colors uppercase font-bold tracking-widest"
43
+ >
44
+ PURGAR & REINICIAR (F5)
45
+ </button>
46
+ </div>
47
+ );
48
+ }
49
+
50
+ return this.props.children;
51
+ }
52
+ }
@@ -0,0 +1,201 @@
1
+ import { useState } from "react";
2
+ import type { KeyboardEvent } from "react";
3
+
4
+ interface InputAreaProps {
5
+ onSend: (text: string) => void;
6
+ disabled?: boolean;
7
+ placeholder?: string;
8
+ suggestions?: string[];
9
+ }
10
+
11
+ export function InputArea({
12
+ onSend,
13
+ disabled,
14
+ placeholder,
15
+ suggestions,
16
+ }: InputAreaProps) {
17
+ const [text, setText] = useState("");
18
+
19
+ const handleSend = () => {
20
+ if (!text.trim() || disabled) return;
21
+ onSend(text);
22
+ setText("");
23
+ // Reset height manually if needed, though state change might cause re-render which resets style if we were not careful.
24
+ // Ideally we reset height via ref, but let's stick to the simple onInput for now.
25
+ };
26
+
27
+ const handleKeyDown = (e: KeyboardEvent<HTMLTextAreaElement>) => {
28
+ if (e.key === "Enter" && !e.shiftKey) {
29
+ e.preventDefault();
30
+ handleSend();
31
+ // Reset height
32
+ (e.target as HTMLTextAreaElement).style.height = "auto";
33
+ }
34
+ };
35
+
36
+ return (
37
+ <div
38
+ style={{
39
+ padding: "1rem 1.5rem 1.5rem",
40
+ borderTop: "1px solid var(--color-border)",
41
+ backgroundColor: "var(--color-bg)",
42
+ display: "flex",
43
+ flexDirection: "column",
44
+ gap: "0.75rem",
45
+ position: "sticky",
46
+ bottom: 0,
47
+ zIndex: 10,
48
+ transition: "background-color 0.2s",
49
+ }}
50
+ >
51
+ {/* Suggestions */}
52
+ {suggestions && suggestions.length > 0 && (
53
+ <div
54
+ style={{
55
+ display: "flex",
56
+ gap: "0.5rem",
57
+ overflowX: "auto",
58
+ paddingBottom: "0.5rem",
59
+ scrollbarWidth: "none", // Hide scrollbar for cleaner look
60
+ }}
61
+ >
62
+ {suggestions.map((s, i) => (
63
+ <button
64
+ key={i}
65
+ onClick={() => onSend(s)}
66
+ disabled={disabled}
67
+ style={{
68
+ fontSize: "var(--text-xs)",
69
+ padding: "0.4rem 1rem",
70
+ backgroundColor: "var(--color-surface)",
71
+ color: "var(--color-text-soft)",
72
+ borderRadius: "999px",
73
+ border: "1px solid var(--color-border)",
74
+ whiteSpace: "nowrap",
75
+ cursor: disabled ? "default" : "pointer",
76
+ transition: "all 0.2s",
77
+ }}
78
+ onMouseEnter={(e) => {
79
+ if (!disabled) {
80
+ e.currentTarget.style.backgroundColor =
81
+ "var(--color-surface-soft)";
82
+ e.currentTarget.style.borderColor = "var(--color-primary)";
83
+ e.currentTarget.style.color = "var(--color-primary)";
84
+ }
85
+ }}
86
+ onMouseLeave={(e) => {
87
+ if (!disabled) {
88
+ e.currentTarget.style.backgroundColor =
89
+ "var(--color-surface)";
90
+ e.currentTarget.style.borderColor = "var(--color-border)";
91
+ e.currentTarget.style.color = "var(--color-text-soft)";
92
+ }
93
+ }}
94
+ >
95
+ {s}
96
+ </button>
97
+ ))}
98
+ </div>
99
+ )}
100
+
101
+ {/* Input Container */}
102
+ <div
103
+ style={{
104
+ display: "flex",
105
+ alignItems: "flex-end",
106
+ gap: "0.75rem",
107
+ backgroundColor: "var(--color-surface)",
108
+ padding: "0.75rem 1rem",
109
+ borderRadius: "1.5rem", // More rounded
110
+ border: "1px solid var(--color-border)",
111
+ transition: "border-color 0.2s, box-shadow 0.2s",
112
+ }}
113
+ onFocus={(e) => {
114
+ e.currentTarget.style.borderColor = "var(--color-primary)";
115
+ e.currentTarget.style.boxShadow = "0 0 0 2px rgba(139, 92, 246, 0.1)";
116
+ }}
117
+ onBlur={(e) => {
118
+ e.currentTarget.style.borderColor = "var(--color-border)";
119
+ e.currentTarget.style.boxShadow = "none";
120
+ }}
121
+ >
122
+ <textarea
123
+ value={text}
124
+ onChange={(e) => setText(e.target.value)}
125
+ onKeyDown={handleKeyDown}
126
+ placeholder={placeholder || "Escribe un mensaje..."}
127
+ disabled={disabled}
128
+ rows={1}
129
+ style={{
130
+ flex: 1,
131
+ resize: "none",
132
+ maxHeight: "120px",
133
+ minHeight: "24px",
134
+ padding: "0.25rem 0",
135
+ background: "transparent",
136
+ border: "none",
137
+ outline: "none",
138
+ fontSize: "var(--text-base)",
139
+ color: "var(--color-text-main)",
140
+ fontFamily: "var(--font-sans)",
141
+ lineHeight: 1.5,
142
+ }}
143
+ onInput={(e) => {
144
+ const target = e.target as HTMLTextAreaElement;
145
+ target.style.height = "auto";
146
+ target.style.height = `${Math.min(target.scrollHeight, 120)}px`;
147
+ }}
148
+ />
149
+ <button
150
+ onClick={handleSend}
151
+ disabled={!text.trim() || disabled}
152
+ style={{
153
+ padding: "0.5rem",
154
+ borderRadius: "50%",
155
+ backgroundColor:
156
+ text.trim() && !disabled
157
+ ? "var(--color-primary)"
158
+ : "var(--color-surface-soft)",
159
+ color:
160
+ text.trim() && !disabled ? "white" : "var(--color-text-soft)",
161
+ display: "flex",
162
+ alignItems: "center",
163
+ justifyContent: "center",
164
+ width: "44px",
165
+ height: "44px",
166
+ transition: "all 0.2s",
167
+ cursor: text.trim() && !disabled ? "pointer" : "default",
168
+ opacity: disabled ? 0.7 : 1,
169
+ }}
170
+ aria-label="Enviar"
171
+ >
172
+ {/* Simple Arrow Icon */}
173
+ <svg
174
+ width="20"
175
+ height="20"
176
+ viewBox="0 0 24 24"
177
+ fill="none"
178
+ stroke="currentColor"
179
+ strokeWidth="2"
180
+ strokeLinecap="round"
181
+ strokeLinejoin="round"
182
+ >
183
+ <line x1="22" y1="2" x2="11" y2="13"></line>
184
+ <polygon points="22 2 15 22 11 13 2 9 22 2"></polygon>
185
+ </svg>
186
+ </button>
187
+ </div>
188
+
189
+ <small
190
+ style={{
191
+ textAlign: "center",
192
+ opacity: 0.6,
193
+ fontSize: "0.75rem",
194
+ color: "var(--color-text-soft)",
195
+ }}
196
+ >
197
+ WADI puede cometer errores. Verifica la información importante.
198
+ </small>
199
+ </div>
200
+ );
201
+ }
@@ -0,0 +1,73 @@
1
+ import { type ReactNode } from "react";
2
+ import { Link, useNavigate } from "react-router-dom";
3
+ import { Sidebar } from "./Sidebar";
4
+ import { useChatStore } from "../store/chatStore";
5
+ import { Menu, Plus } from "lucide-react";
6
+
7
+ interface LayoutProps {
8
+ children: ReactNode;
9
+ }
10
+
11
+ export function Layout({ children }: LayoutProps) {
12
+ const { isSidebarOpen, setSidebarOpen, toggleSidebar, resetChat } =
13
+ useChatStore();
14
+ const navigate = useNavigate();
15
+
16
+ const handleNewChat = () => {
17
+ resetChat();
18
+ navigate("/chat");
19
+ setSidebarOpen(false);
20
+ };
21
+
22
+ return (
23
+ <div className="flex w-full h-screen min-h-screen relative overflow-hidden bg-[var(--wadi-bg)] text-[var(--wadi-text)]">
24
+ {/* Mobile Sidebar Overlay */}
25
+ {isSidebarOpen && (
26
+ <div
27
+ className="lg:hidden fixed inset-0 z-40 bg-black/50 backdrop-blur-sm transition-opacity"
28
+ onClick={() => setSidebarOpen(false)}
29
+ />
30
+ )}
31
+
32
+ {/* Sidebar - Visible on Desktop, Drawer on Mobile */}
33
+ <div
34
+ className={`fixed lg:relative z-50 h-full transition-transform duration-300 lg:transform-none ${
35
+ isSidebarOpen ? "translate-x-0" : "-translate-x-full lg:translate-x-0"
36
+ }`}
37
+ >
38
+ <Sidebar isOpen={isSidebarOpen} onClose={() => setSidebarOpen(false)} />
39
+ </div>
40
+
41
+ <div className="flex-1 flex flex-col relative w-full overflow-hidden bg-[var(--wadi-bg)]">
42
+ {/* Mobile Top Bar */}
43
+ <div className="lg:hidden h-[60px] border-b border-[var(--wadi-border)] bg-[var(--wadi-surface)] backdrop-blur-md flex items-center justify-between px-4 shrink-0 z-30 shadow-sm relative">
44
+ <div className="flex items-center gap-3">
45
+ <button
46
+ onClick={toggleSidebar}
47
+ className="text-[var(--wadi-text-dim)] p-2 rounded-lg hover:bg-[var(--wadi-surface-hover)] transition-colors"
48
+ aria-label="Abrir menú"
49
+ >
50
+ <Menu size={20} />
51
+ </button>
52
+ <Link to="/" className="flex items-center gap-2 no-underline">
53
+ <span className="font-bold text-lg text-[var(--wadi-text)] tracking-tight">
54
+ WADI
55
+ </span>
56
+ </Link>
57
+ </div>
58
+
59
+ <button
60
+ onClick={handleNewChat}
61
+ className="p-2 bg-[var(--wadi-primary)] text-white rounded-full shadow-md active:scale-95 transition-transform"
62
+ >
63
+ <Plus size={20} />
64
+ </button>
65
+ </div>
66
+
67
+ <main className="flex-1 overflow-hidden relative flex flex-col">
68
+ {children}
69
+ </main>
70
+ </div>
71
+ </div>
72
+ );
73
+ }
@@ -0,0 +1,66 @@
1
+ import type { Attachment } from "../store/chatStore";
2
+
3
+ interface MessageBubbleProps {
4
+ role: "user" | "assistant";
5
+ content: string;
6
+ timestamp?: string;
7
+ attachments?: Attachment[];
8
+ }
9
+
10
+ export function MessageBubble({
11
+ role,
12
+ content,
13
+ timestamp,
14
+ }: MessageBubbleProps) {
15
+ const isUser = role === "user";
16
+
17
+ return (
18
+ <div
19
+ className={`flex flex-col mb-4 w-full ${isUser ? "items-end" : "items-start"}`}
20
+ >
21
+ <div className={isUser ? "bubble-user" : "bubble-wadi"}>
22
+ {isUser ? (
23
+ <div className="whitespace-pre-wrap">{content}</div>
24
+ ) : (
25
+ <div>
26
+ {content.split(/(?=^#{1,3}\s)/m).map((block, i) => {
27
+ // Simple markdown header parsing for WADI structure
28
+ const match = block.match(/^(#{1,3})\s+(.+)(\r?\n|$)/);
29
+ if (match) {
30
+ const title = match[2].trim();
31
+ const body = block.replace(match[0], "").trim();
32
+ return (
33
+ <div key={i} className="mb-4 last:mb-0">
34
+ <div className="font-bold text-[1.05em] mb-1 text-[var(--wadi-primary)]">
35
+ {title}
36
+ </div>
37
+ <div className="whitespace-pre-wrap leading-relaxed text-[var(--wadi-text-muted)] font-light">
38
+ {body}
39
+ </div>
40
+ </div>
41
+ );
42
+ }
43
+ return (
44
+ <div
45
+ key={i}
46
+ className="whitespace-pre-wrap leading-relaxed text-[var(--wadi-text-muted)] font-light"
47
+ >
48
+ {block.trim()}
49
+ </div>
50
+ );
51
+ })}
52
+ </div>
53
+ )}
54
+ </div>
55
+
56
+ {timestamp && (
57
+ <span className="text-[10px] text-[var(--wadi-text-dim)] px-2 mt-1">
58
+ {new Date(timestamp).toLocaleTimeString([], {
59
+ hour: "2-digit",
60
+ minute: "2-digit",
61
+ })}
62
+ </span>
63
+ )}
64
+ </div>
65
+ );
66
+ }
@@ -0,0 +1,108 @@
1
+ import { Button } from "./common/Button";
2
+
3
+ interface OnboardingModalProps {
4
+ onComplete: () => void;
5
+ }
6
+
7
+ export function OnboardingModal({ onComplete }: OnboardingModalProps) {
8
+ return (
9
+ <div
10
+ style={{
11
+ position: "fixed",
12
+ inset: 0,
13
+ backgroundColor: "var(--color-bg)",
14
+ zIndex: 9999,
15
+ display: "flex",
16
+ alignItems: "center",
17
+ justifyContent: "center",
18
+ padding: "1.5rem",
19
+ }}
20
+ >
21
+ <div
22
+ style={{
23
+ maxWidth: "420px",
24
+ width: "100%",
25
+ display: "flex",
26
+ flexDirection: "column",
27
+ gap: "2rem",
28
+ alignItems: "flex-start", // Left align as per "serious" look usually, but centering might be better for intro. Let's try centered for focus.
29
+ textAlign: "left", // Actually, serious text looks good left-aligned often, but let's stick to a clean centered or left layout.
30
+ // User didn't specify alignment, but "Copy textual" suggests reading.
31
+ // Let's center the container but maybe left align text for readability?
32
+ // "Título: WADI no charla..." - Centered is probably safer for a splash screen.
33
+ }}
34
+ >
35
+ <div style={{ display: "flex", flexDirection: "column", gap: "1rem" }}>
36
+ <h1
37
+ style={{
38
+ fontSize: "2rem",
39
+ fontWeight: 700,
40
+ lineHeight: "1.1",
41
+ margin: 0,
42
+ background: "var(--grad-main)",
43
+ WebkitBackgroundClip: "text",
44
+ WebkitTextFillColor: "transparent",
45
+ }}
46
+ >
47
+ WADI no charla.
48
+ <br />
49
+ Piensa con vos.
50
+ </h1>
51
+
52
+ <div
53
+ style={{
54
+ fontSize: "1.1rem",
55
+ color: "var(--color-text-soft)",
56
+ lineHeight: "1.6",
57
+ display: "flex",
58
+ flexDirection: "column",
59
+ gap: "0.25rem",
60
+ }}
61
+ >
62
+ <p style={{ margin: 0 }}>No hace magia.</p>
63
+ <p style={{ margin: 0 }}>No valida ideas flojas.</p>
64
+ <p style={{ margin: 0 }}>
65
+ Si algo está desordenado, lo ordena avanzando.
66
+ </p>
67
+ </div>
68
+ </div>
69
+
70
+ <div
71
+ style={{ width: "100%", borderTop: "1px solid var(--color-border)" }}
72
+ />
73
+
74
+ <div
75
+ style={{
76
+ width: "100%",
77
+ display: "flex",
78
+ flexDirection: "column",
79
+ gap: "1.5rem",
80
+ }}
81
+ >
82
+ <p
83
+ style={{
84
+ margin: 0,
85
+ color: "var(--color-text-main)",
86
+ fontWeight: 500,
87
+ }}
88
+ >
89
+ Escribí lo primero que tengas. Aunque esté mal.
90
+ </p>
91
+
92
+ <Button
93
+ variant="primary"
94
+ onClick={onComplete}
95
+ style={{
96
+ width: "100%",
97
+ justifyContent: "center",
98
+ padding: "1rem",
99
+ fontSize: "1.1rem",
100
+ }}
101
+ >
102
+ Empezar
103
+ </Button>
104
+ </div>
105
+ </div>
106
+ </div>
107
+ );
108
+ }
@@ -0,0 +1,187 @@
1
+ import { useState } from "react";
2
+ import { useChatStore, type ChatMode } from "../store/chatStore";
3
+
4
+ interface SettingsModalProps {
5
+ onClose: () => void;
6
+ }
7
+
8
+ export function SettingsModal({ onClose }: SettingsModalProps) {
9
+ const { settings, updateSettings, exportData, clearAllChats } =
10
+ useChatStore();
11
+ const [activeTab, setActiveTab] = useState<"general" | "persona" | "data">(
12
+ "general"
13
+ );
14
+
15
+ return (
16
+ <div className="fixed inset-0 z-[200] flex items-center justify-center bg-black/80 backdrop-blur-sm animate-in fade-in duration-200">
17
+ <div className="w-[90%] max-w-[500px] bg-[var(--wadi-bg)] border border-[var(--wadi-primary)] shadow-[0_0_30px_rgba(var(--wadi-primary-rgb),0.2)] rounded-lg overflow-hidden flex flex-col h-[500px]">
18
+ {/* Header */}
19
+ <div className="p-4 border-b border-[var(--wadi-border)] flex justify-between items-center bg-[var(--wadi-surface)]/50">
20
+ <h2 className="font-mono-wadi text-sm font-bold text-[var(--wadi-primary)] uppercase tracking-wider">
21
+ [CONFIGURACIÓN_DEL_SISTEMA]
22
+ </h2>
23
+ <button
24
+ onClick={onClose}
25
+ className="text-[var(--wadi-text-secondary)] hover:text-[var(--wadi-text)] transition-colors text-xs"
26
+ >
27
+ [X]
28
+ </button>
29
+ </div>
30
+
31
+ {/* Tabs */}
32
+ <div className="flex border-b border-[var(--wadi-border)] bg-[var(--wadi-bg)]">
33
+ {(["general", "persona", "data"] as const).map((tab) => (
34
+ <button
35
+ key={tab}
36
+ onClick={() => setActiveTab(tab)}
37
+ className={`flex-1 py-3 text-xs font-mono-wadi uppercase tracking-widest transition-colors ${
38
+ activeTab === tab
39
+ ? "bg-[var(--wadi-primary)] text-black font-bold"
40
+ : "text-[var(--wadi-text-muted)] hover:text-[var(--wadi-text)] hover:bg-[var(--wadi-surface)]"
41
+ }`}
42
+ >
43
+ {tab}
44
+ </button>
45
+ ))}
46
+ </div>
47
+
48
+ {/* Content */}
49
+ <div className="flex-1 overflow-y-auto p-6 space-y-8 custom-scrollbar">
50
+ {activeTab === "general" && (
51
+ <div className="space-y-6">
52
+ <div className="space-y-2">
53
+ <label className="text-xs font-mono-wadi text-[var(--wadi-text-secondary)] uppercase">
54
+ Idioma de Interfaz
55
+ </label>
56
+ <div className="flex gap-2">
57
+ <button
58
+ onClick={() => updateSettings({ language: "es" })}
59
+ className={`flex-1 px-4 py-2 border rounded text-xs font-mono-wadi transition-all ${
60
+ settings.language === "es"
61
+ ? "border-[var(--wadi-primary)] bg-[var(--wadi-primary)]/10 text-[var(--wadi-primary)]"
62
+ : "border-[var(--wadi-border)] text-[var(--wadi-text-muted)] hover:border-[var(--wadi-text-muted)]"
63
+ }`}
64
+ >
65
+ Español
66
+ </button>
67
+ <button
68
+ onClick={() => updateSettings({ language: "en" })}
69
+ className={`flex-1 px-4 py-2 border rounded text-xs font-mono-wadi transition-all ${
70
+ settings.language === "en"
71
+ ? "border-[var(--wadi-primary)] bg-[var(--wadi-primary)]/10 text-[var(--wadi-primary)]"
72
+ : "border-[var(--wadi-border)] text-[var(--wadi-text-muted)] hover:border-[var(--wadi-text-muted)]"
73
+ }`}
74
+ >
75
+ English
76
+ </button>
77
+ </div>
78
+ </div>
79
+
80
+ <div className="space-y-2">
81
+ <label className="text-xs font-mono-wadi text-[var(--wadi-text-secondary)] uppercase">
82
+ Tema Visual
83
+ </label>
84
+ <div className="grid grid-cols-3 gap-2">
85
+ {(["light", "dark", "system"] as const).map((theme) => (
86
+ <button
87
+ key={theme}
88
+ onClick={() => updateSettings({ theme })}
89
+ className={`px-2 py-2 border rounded text-xs font-mono-wadi transition-all uppercase ${
90
+ settings.theme === theme
91
+ ? "border-[var(--wadi-primary)] bg-[var(--wadi-primary)]/10 text-[var(--wadi-primary)]"
92
+ : "border-[var(--wadi-border)] text-[var(--wadi-text-muted)] hover:border-[var(--wadi-text-muted)]"
93
+ }`}
94
+ >
95
+ {theme}
96
+ </button>
97
+ ))}
98
+ </div>
99
+ </div>
100
+ </div>
101
+ )}
102
+
103
+ {activeTab === "persona" && (
104
+ <div className="space-y-6">
105
+ <div className="space-y-2">
106
+ <div className="flex justify-between items-center mb-1">
107
+ <label className="text-xs font-mono-wadi text-[var(--wadi-text-secondary)] uppercase">
108
+ Nivel de Sarcasmo
109
+ </label>
110
+ <span className="text-xs font-mono-wadi text-[var(--wadi-primary)]">
111
+ {settings.sarcasmLevel}%
112
+ </span>
113
+ </div>
114
+ <input
115
+ type="range"
116
+ min="0"
117
+ max="100"
118
+ step="10"
119
+ value={settings.sarcasmLevel}
120
+ onChange={(e) =>
121
+ updateSettings({ sarcasmLevel: parseInt(e.target.value) })
122
+ }
123
+ className="w-full h-1 bg-[var(--wadi-border)] rounded-lg appearance-none cursor-pointer accent-[var(--wadi-primary)]"
124
+ />
125
+ <div className="flex justify-between text-[10px] text-[var(--wadi-text-muted)] font-mono-wadi">
126
+ <span>Sutil</span>
127
+ <span>Nuclear</span>
128
+ </div>
129
+ </div>
130
+
131
+ <div className="space-y-2">
132
+ <label className="text-xs font-mono-wadi text-[var(--wadi-text-secondary)] uppercase">
133
+ Modo por Defecto
134
+ </label>
135
+ <select
136
+ value={settings.defaultMode}
137
+ onChange={(e) =>
138
+ updateSettings({ defaultMode: e.target.value as ChatMode })
139
+ }
140
+ className="w-full bg-[var(--wadi-surface)] border border-[var(--wadi-border)] text-[var(--wadi-text)] text-xs p-3 rounded font-mono-wadi outline-none focus:border-[var(--wadi-primary)]"
141
+ >
142
+ <option value="normal">General (Compañero)</option>
143
+ <option value="tech">Tech (Técnico)</option>
144
+ <option value="biz">Biz (Negocios)</option>
145
+ <option value="tutor">Tutor (Aprendizaje)</option>
146
+ </select>
147
+ </div>
148
+ </div>
149
+ )}
150
+
151
+ {activeTab === "data" && (
152
+ <div className="space-y-4">
153
+ <button
154
+ onClick={exportData}
155
+ className="w-full flex items-center justify-between p-4 border border-[var(--wadi-border)] rounded hover:bg-[var(--wadi-surface)] transition-colors group"
156
+ >
157
+ <span className="text-xs font-mono-wadi text-[var(--wadi-text)] uppercase">
158
+ Exportar mis datos (JSON)
159
+ </span>
160
+ <span className="text-[var(--wadi-text-muted)] group-hover:text-[var(--wadi-primary)]">
161
+
162
+ </span>
163
+ </button>
164
+
165
+ <button
166
+ onClick={() => {
167
+ if (
168
+ confirm("ESTO ES IRREVERSIBLE. ¿Borrar TODA la historia?")
169
+ ) {
170
+ clearAllChats();
171
+ onClose();
172
+ }
173
+ }}
174
+ className="w-full flex items-center justify-between p-4 border border-[var(--wadi-alert)]/30 rounded hover:bg-[var(--wadi-alert)]/10 transition-colors group text-[var(--wadi-alert)]"
175
+ >
176
+ <span className="text-xs font-mono-wadi uppercase">
177
+ Quemar Archivos (Borrar Todo)
178
+ </span>
179
+ <span className="group-hover:animate-pulse">⚠️</span>
180
+ </button>
181
+ </div>
182
+ )}
183
+ </div>
184
+ </div>
185
+ </div>
186
+ );
187
+ }