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,64 @@
1
+ import { FileText } from "lucide-react";
2
+ import type { HTMLAttributes } from "react";
3
+
4
+ interface LogItemProps extends HTMLAttributes<HTMLDivElement> {
5
+ date: string;
6
+ title: string;
7
+ isActive?: boolean;
8
+ onDelete?: (e: React.MouseEvent) => void;
9
+ onReport?: (e: React.MouseEvent) => void;
10
+ }
11
+
12
+ export function LogItem({
13
+ date,
14
+ title,
15
+ isActive,
16
+ onDelete,
17
+ onReport,
18
+ onClick,
19
+ className,
20
+ }: LogItemProps) {
21
+ return (
22
+ <div
23
+ onClick={onClick}
24
+ className={`
25
+ group relative flex items-center justify-between
26
+ px-4 py-2 cursor-pointer transition-all duration-150
27
+ border-l-2 select-none
28
+ ${
29
+ isActive
30
+ ? "bg-[var(--wadi-surface)] border-[var(--wadi-primary)] text-[var(--wadi-text)]"
31
+ : "border-transparent text-[var(--wadi-text-muted)] hover:bg-[var(--wadi-surface)] hover:text-[var(--wadi-text)] hover:border-[var(--wadi-border)]"
32
+ }
33
+ ${className || ""}
34
+ `}
35
+ >
36
+ <div className="flex items-baseline gap-2 overflow-hidden font-mono-wadi text-[11px] w-full">
37
+ <span className="opacity-50 shrink-0">[{date}]</span>
38
+ <span className="truncate opacity-90 uppercase tracking-tight">
39
+ {title}
40
+ </span>
41
+ </div>
42
+
43
+ {onReport && (
44
+ <button
45
+ onClick={onReport}
46
+ className="absolute right-8 opacity-0 group-hover:opacity-100 hover:text-[var(--wadi-primary)] transition-opacity mr-2"
47
+ title="Audit Report"
48
+ >
49
+ <FileText size={14} />
50
+ </button>
51
+ )}
52
+
53
+ {onDelete && (
54
+ <button
55
+ onClick={onDelete}
56
+ className="absolute right-2 opacity-0 group-hover:opacity-100 hover:text-[var(--wadi-alert)] transition-opacity"
57
+ title="Purge Log"
58
+ >
59
+
60
+ </button>
61
+ )}
62
+ </div>
63
+ );
64
+ }
@@ -0,0 +1,40 @@
1
+ import React from "react";
2
+
3
+ /**
4
+ * WADI BUTTON
5
+ * Estética Modern Dark.
6
+ */
7
+ interface MondayButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
8
+ variant?: "primary" | "danger" | "ghost";
9
+ label: string;
10
+ }
11
+
12
+ export const MondayButton: React.FC<MondayButtonProps> = ({
13
+ variant = "primary",
14
+ label,
15
+ className = "",
16
+ ...props
17
+ }) => {
18
+ const baseStyle =
19
+ "relative font-mono text-xs uppercase tracking-widest transition-all duration-200 border touch-min-44 px-6";
20
+
21
+ const variants = {
22
+ primary:
23
+ "bg-[var(--wadi-primary-glow)] border-[var(--wadi-primary)] text-[var(--wadi-primary)] hover:bg-[var(--wadi-primary)] hover:text-white hover:shadow-[0_0_20px_var(--wadi-primary-dim)]",
24
+ danger:
25
+ "bg-red-900/20 border-[var(--wadi-red)] text-[var(--wadi-red)] hover:bg-[var(--wadi-red)] hover:text-white hover:shadow-[0_0_20px_rgba(239,68,68,0.4)]",
26
+ ghost:
27
+ "bg-transparent border-transparent text-gray-500 hover:text-[var(--wadi-text)]",
28
+ };
29
+
30
+ return (
31
+ <button
32
+ className={`${baseStyle} ${variants[variant]} ${className}`}
33
+ {...props}
34
+ >
35
+ <span className="relative z-10">{label}</span>
36
+ {/* Tech Corner Decoration */}
37
+ <div className="absolute top-0 right-0 w-2 h-2 border-t border-r border-current opacity-50"></div>
38
+ </button>
39
+ );
40
+ };
@@ -0,0 +1,24 @@
1
+ import React, { type ReactNode } from "react";
2
+
3
+ interface MondayCardProps {
4
+ children: ReactNode;
5
+ className?: string;
6
+ title?: string;
7
+ }
8
+
9
+ export const MondayCard: React.FC<MondayCardProps> = ({
10
+ children,
11
+ className = "",
12
+ title,
13
+ }) => {
14
+ return (
15
+ <div className={`monday-card flex flex-col ${className}`}>
16
+ {title && (
17
+ <div className="absolute top-0 left-0 bg-[var(--wadi-primary)] text-black px-2 py-0.5 text-[10px] font-bold font-mono uppercase tracking-widest">
18
+ {title}
19
+ </div>
20
+ )}
21
+ {children}
22
+ </div>
23
+ );
24
+ };
@@ -0,0 +1,208 @@
1
+ import { useRef, useEffect } from "react";
2
+ import { useNavigate } from "react-router-dom";
3
+ import { useChatStore } from "../../store/chatStore";
4
+ import { useScouter } from "../../hooks/useScouter";
5
+
6
+ interface ScouterProps {
7
+ isDecisionBlocked?: boolean;
8
+ }
9
+
10
+ export function Scouter({ isDecisionBlocked = false }: ScouterProps) {
11
+ const messages = useChatStore((state) => state.messages);
12
+ const rank = useChatStore((state) => state.rank);
13
+ const systemDeath = useChatStore((state) => state.systemDeath);
14
+ const resetChat = useChatStore((state) => state.resetChat);
15
+ const navigate = useNavigate();
16
+
17
+ const {
18
+ playAlertSound,
19
+ playScanSound,
20
+ initAmbientHum,
21
+ setAmbientIntensity,
22
+ playDeathSound,
23
+ playYawnSound,
24
+ } = useScouter();
25
+ const prevMessagesLength = useRef(messages.length);
26
+ const prevRank = useRef(rank);
27
+
28
+ const visualAlertTimestamp = useChatStore(
29
+ (state) => state.visualAlertTimestamp
30
+ );
31
+ const prevVisualAlertTimestamp = useRef(visualAlertTimestamp);
32
+
33
+ // Trigger Visual Alert on Timestamp Change
34
+ useEffect(() => {
35
+ if (visualAlertTimestamp !== prevVisualAlertTimestamp.current) {
36
+ playAlertSound();
37
+ const flashOverlay = document.getElementById("scouter-flash-overlay");
38
+ if (flashOverlay) {
39
+ // Alerta roja explícita para errores de validación
40
+ flashOverlay.style.background = "var(--wadi-alert)";
41
+ setTimeout(() => {
42
+ flashOverlay.style.opacity = "0.6";
43
+ }, 100);
44
+ setTimeout(() => {
45
+ flashOverlay.style.opacity = "0";
46
+ }, 800);
47
+ }
48
+ prevVisualAlertTimestamp.current = visualAlertTimestamp;
49
+ }
50
+ }, [visualAlertTimestamp, playAlertSound]);
51
+
52
+ const scornTimestamp = useChatStore((state) => state.scornTimestamp);
53
+ const prevScornTimestamp = useRef(scornTimestamp);
54
+
55
+ // Trigger Scorn Alert (Lavender Flash + Yawn Sound)
56
+ useEffect(() => {
57
+ if (scornTimestamp !== prevScornTimestamp.current) {
58
+ playYawnSound(); // Electronic Yawn
59
+ const flashOverlay = document.getElementById("scouter-flash-overlay");
60
+ if (flashOverlay) {
61
+ flashOverlay.style.background = "#A78BFA"; // Lavender
62
+ setTimeout(() => {
63
+ flashOverlay.style.opacity = "0.5";
64
+ }, 100);
65
+ setTimeout(() => {
66
+ flashOverlay.style.opacity = "0";
67
+ }, 800);
68
+ }
69
+ prevScornTimestamp.current = scornTimestamp;
70
+ }
71
+ }, [scornTimestamp, playYawnSound]);
72
+
73
+ // Initialize Ambient Hum on Mount
74
+ useEffect(() => {
75
+ const handleInteraction = () => initAmbientHum();
76
+ window.addEventListener("click", handleInteraction, { once: true });
77
+ window.addEventListener("keydown", handleInteraction, { once: true });
78
+ return () => {
79
+ window.removeEventListener("click", handleInteraction);
80
+ window.removeEventListener("keydown", handleInteraction);
81
+ };
82
+ }, [initAmbientHum]);
83
+
84
+ useEffect(() => {
85
+ if (systemDeath) {
86
+ setAmbientIntensity("high");
87
+ return;
88
+ }
89
+ // Si hay check de lucidez, el ambiente sube pero no es hostil
90
+ setAmbientIntensity(isDecisionBlocked ? "high" : "normal");
91
+ }, [isDecisionBlocked, systemDeath, setAmbientIntensity]);
92
+
93
+ // System Death Logic (Keep as fail-safe)
94
+ useEffect(() => {
95
+ if (systemDeath) {
96
+ playDeathSound();
97
+ const overlay = document.getElementById("scouter-flash-overlay");
98
+ let active = true;
99
+ const loop = () => {
100
+ if (!active || !overlay) return;
101
+ overlay.style.opacity = Math.random() > 0.5 ? "0.8" : "0.2";
102
+ setTimeout(loop, 100);
103
+ };
104
+ loop();
105
+
106
+ const timer = setTimeout(() => {
107
+ resetChat();
108
+ useChatStore.setState({
109
+ systemDeath: false,
110
+ rank: "GENERADOR_DE_HUMO",
111
+ points: 0,
112
+ });
113
+ navigate("/");
114
+ }, 10000);
115
+
116
+ return () => {
117
+ active = false;
118
+ clearTimeout(timer);
119
+ if (overlay) overlay.style.opacity = "0";
120
+ };
121
+ }
122
+ }, [systemDeath, navigate, resetChat, playDeathSound]);
123
+
124
+ // Rank Change Logic (Visual pulse)
125
+ useEffect(() => {
126
+ if (prevRank.current !== rank) {
127
+ playScanSound();
128
+ const overlay = document.getElementById("scouter-flash-overlay");
129
+ if (overlay) {
130
+ overlay.style.backgroundColor = "var(--wadi-primary)";
131
+ overlay.style.opacity = "0.3"; // Más suave
132
+ setTimeout(() => {
133
+ overlay.style.opacity = "0";
134
+ overlay.style.backgroundColor = "var(--wadi-alert)";
135
+ }, 1500);
136
+ }
137
+ }
138
+ prevRank.current = rank;
139
+ }, [rank, playScanSound]);
140
+
141
+ useEffect(() => {
142
+ const newCount = messages.length;
143
+ const oldCount = prevMessagesLength.current;
144
+
145
+ if (newCount > oldCount) {
146
+ const lastMsg = messages[newCount - 1];
147
+ const isMyMessage = lastMsg.role === "user";
148
+
149
+ // SCOUTER LOGIC
150
+ if (!isMyMessage) {
151
+ const text = lastMsg.content || "";
152
+ const isChaotic = text.includes("[ALERTA DE CAOS DETECTADA]");
153
+ const isLucidityCheck =
154
+ text.includes("[CHECK_DE_LUCIDEZ]") ||
155
+ text.includes("[FORCE_DECISION]"); // Backwards compat
156
+ const isAnalysis =
157
+ text.includes("Analizar") || text.includes("[DECONSTRUCT_START]");
158
+
159
+ if (isChaotic || isLucidityCheck) {
160
+ playAlertSound();
161
+ const flashOverlay = document.getElementById("scouter-flash-overlay");
162
+ if (flashOverlay) {
163
+ // Si es Lucidez, color lavanda suave, no rojo
164
+ if (isLucidityCheck) {
165
+ flashOverlay.style.background = "var(--wadi-primary)";
166
+ } else {
167
+ flashOverlay.style.background = "var(--wadi-alert)";
168
+ }
169
+ setTimeout(() => {
170
+ flashOverlay.style.opacity = "0.4";
171
+ }, 100);
172
+ setTimeout(() => {
173
+ flashOverlay.style.opacity = "0";
174
+ flashOverlay.style.background = "var(--wadi-alert)"; // reset
175
+ }, 1000);
176
+ }
177
+ } else if (isAnalysis) {
178
+ playScanSound();
179
+ }
180
+ }
181
+ }
182
+ prevMessagesLength.current = newCount;
183
+ }, [messages, playAlertSound, playScanSound]);
184
+
185
+ return (
186
+ <>
187
+ <div
188
+ id="scouter-flash-overlay"
189
+ className="fixed inset-0 pointer-events-none z-[9999] bg-[var(--wadi-alert)] opacity-0 transition-opacity duration-1000 mix-blend-screen" // blend screen is softer
190
+ aria-hidden="true"
191
+ />
192
+
193
+ {systemDeath && (
194
+ <div className="fixed inset-0 z-[10000] pointer-events-auto bg-black/80 flex items-center justify-center overflow-hidden">
195
+ <div className="text-[var(--wadi-alert)] font-bold text-4xl animate-pulse font-mono-wadi tracking-widest text-center">
196
+ DESCONEXIÓN VITAL
197
+ <br />
198
+ PROTOCOL_RESET
199
+ <br />
200
+ <span className="text-sm text-white mt-4 block">
201
+ Reiniciando búnker...
202
+ </span>
203
+ </div>
204
+ </div>
205
+ )}
206
+ </>
207
+ );
208
+ }
@@ -0,0 +1,202 @@
1
+ import { useState, useEffect, useRef } from "react";
2
+ import { type Attachment } from "../../store/chatStore";
3
+ import { useScouter } from "../../hooks/useScouter";
4
+ import { Paperclip } from "lucide-react";
5
+
6
+ // Fallback icons if lucide not imported/working in this context, but user mentioned "iconos minimalistas"
7
+ const IconSend = () => (
8
+ <svg
9
+ width="20"
10
+ height="20"
11
+ viewBox="0 0 24 24"
12
+ fill="none"
13
+ stroke="currentColor"
14
+ strokeWidth="2"
15
+ strokeLinecap="round"
16
+ strokeLinejoin="round"
17
+ >
18
+ <path d="m22 2-7 20-4-9-9-4Z" />
19
+ <path d="M22 2 11 13" />
20
+ </svg>
21
+ );
22
+
23
+ const IconAttach = () => (
24
+ <svg
25
+ width="20"
26
+ height="20"
27
+ viewBox="0 0 24 24"
28
+ fill="none"
29
+ stroke="currentColor"
30
+ strokeWidth="2"
31
+ strokeLinecap="round"
32
+ strokeLinejoin="round"
33
+ >
34
+ <path d="m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48" />
35
+ </svg>
36
+ );
37
+
38
+ interface TerminalInputProps {
39
+ onSendMessage: (text: string, attachments: Attachment[]) => Promise<void>;
40
+ isLoading: boolean;
41
+ isDecisionBlocked?: boolean;
42
+ activeFocus?: string | null;
43
+ }
44
+
45
+ export function TerminalInput({
46
+ onSendMessage,
47
+ isLoading,
48
+ activeFocus,
49
+ }: TerminalInputProps) {
50
+ const [input, setInput] = useState("");
51
+ const [selectedFile, setSelectedFile] = useState<File | null>(null);
52
+ const fileInputRef = useRef<HTMLInputElement>(null);
53
+ const inputRef = useRef<HTMLInputElement>(null);
54
+
55
+ const { playScanSound } = useScouter();
56
+
57
+ // FOCUS LAW: Keep input focused always
58
+ useEffect(() => {
59
+ if (!isLoading && inputRef.current) {
60
+ inputRef.current.focus();
61
+ }
62
+ }, [isLoading, input, selectedFile]);
63
+
64
+ const handleSend = async (e?: React.FormEvent) => {
65
+ e?.preventDefault();
66
+ if ((!input.trim() && !selectedFile) || isLoading) return;
67
+
68
+ let finalPrompt = input;
69
+ const finalAttachments: Attachment[] = [];
70
+
71
+ const prevInput = input;
72
+ setInput("");
73
+ setSelectedFile(null);
74
+ if (fileInputRef.current) fileInputRef.current.value = "";
75
+
76
+ // Force focus back
77
+ if (inputRef.current) inputRef.current.focus();
78
+
79
+ if (selectedFile) {
80
+ const isText =
81
+ selectedFile.type.startsWith("text/") ||
82
+ selectedFile.name.endsWith(".md") ||
83
+ selectedFile.name.endsWith(".csv") ||
84
+ selectedFile.name.endsWith(".json");
85
+
86
+ if (isText) {
87
+ try {
88
+ const textContent = await selectedFile.text();
89
+ finalPrompt += `\n\n[ARCHIVO IMPLÍCITO: ${selectedFile.name}]\n---\n${textContent}\n---`;
90
+ } catch (err) {
91
+ console.error("Error reading text file", err);
92
+ }
93
+ } else {
94
+ try {
95
+ const base64 = await new Promise<string>((resolve, reject) => {
96
+ const reader = new FileReader();
97
+ reader.onload = () => resolve(reader.result as string);
98
+ reader.onerror = reject;
99
+ reader.readAsDataURL(selectedFile);
100
+ });
101
+ finalAttachments.push({
102
+ url: base64,
103
+ name: selectedFile.name,
104
+ type: selectedFile.type,
105
+ });
106
+ } catch (err) {
107
+ console.error("Error converting file to base64", err);
108
+ }
109
+ }
110
+ }
111
+
112
+ try {
113
+ await onSendMessage(finalPrompt, finalAttachments);
114
+ } catch (err) {
115
+ console.error("Send failed", err);
116
+ setInput(prevInput);
117
+ }
118
+ };
119
+
120
+ const handleFileSelect = (e: React.ChangeEvent<HTMLInputElement>) => {
121
+ const file = e.target.files?.[0];
122
+ if (!file) return;
123
+ playScanSound();
124
+ setSelectedFile(file);
125
+ };
126
+
127
+ return (
128
+ <div className="w-full max-w-3xl mx-auto flex flex-col gap-2 relative mb-6">
129
+ {/* File Preview Capsule */}
130
+ {selectedFile && (
131
+ <div className="absolute -top-12 left-0 right-0 flex justify-center animate-in fade-in slide-in-from-bottom-2">
132
+ <div className="bg-white/90 backdrop-blur border border-purple-100 shadow-sm rounded-full px-4 py-1 flex items-center gap-2 text-xs text-purple-600 font-medium">
133
+ <Paperclip size={12} className="opacity-50" />
134
+ <span>{selectedFile.name}</span>
135
+ <button
136
+ onClick={() => setSelectedFile(null)}
137
+ className="ml-2 hover:bg-purple-50 rounded-full p-1 transition-colors"
138
+ >
139
+ x
140
+ </button>
141
+ </div>
142
+ </div>
143
+ )}
144
+
145
+ {/* Main Input Capsule */}
146
+ <form onSubmit={handleSend} className="relative w-full">
147
+ <div className="neo-capsule flex items-center gap-2 pr-2 overflow-hidden">
148
+ <input
149
+ type="file"
150
+ ref={fileInputRef}
151
+ className="hidden"
152
+ onChange={handleFileSelect}
153
+ accept="image/*,.txt,.md,.pdf,.csv,.json"
154
+ />
155
+
156
+ <button
157
+ type="button"
158
+ className="p-2 text-gray-400 hover:text-purple-500 transition-colors rounded-full hover:bg-[var(--wadi-surface-hover)]"
159
+ onClick={() => fileInputRef.current?.click()}
160
+ disabled={isLoading}
161
+ >
162
+ <IconAttach />
163
+ </button>
164
+
165
+ <input
166
+ id="wadi-user-input"
167
+ name="userInput"
168
+ type="text"
169
+ ref={inputRef}
170
+ value={input}
171
+ onChange={(e) => setInput(e.target.value)}
172
+ placeholder={
173
+ activeFocus
174
+ ? "Estamos en un foco activo. ¿Qué opinás?"
175
+ : "Preguntá o instruye a WADI..."
176
+ }
177
+ className="flex-1 bg-transparent border-none outline-none text-[var(--wadi-text)] placeholder:text-[var(--wadi-text-dim)] text-sm font-medium h-full min-h-[24px]"
178
+ autoComplete="off"
179
+ onKeyDown={(e) => {
180
+ if (e.key === "Enter" && !e.shiftKey) {
181
+ e.preventDefault();
182
+ handleSend();
183
+ }
184
+ }}
185
+ />
186
+
187
+ <button
188
+ type="submit"
189
+ disabled={(!input.trim() && !selectedFile) || isLoading}
190
+ className={`p-2 rounded-full transition-all duration-300 ${
191
+ input.trim() || selectedFile
192
+ ? "bg-[var(--wadi-primary)] text-white shadow-md hover:scale-105"
193
+ : "bg-gray-100/10 text-gray-500"
194
+ }`}
195
+ >
196
+ <IconSend />
197
+ </button>
198
+ </div>
199
+ </form>
200
+ </div>
201
+ );
202
+ }
@@ -0,0 +1,67 @@
1
+ import React, { useState } from "react";
2
+
3
+ interface TooltipProps {
4
+ content: string;
5
+ children: React.ReactNode;
6
+ position?: "top" | "bottom" | "left" | "right";
7
+ delay?: number;
8
+ }
9
+
10
+ export function Tooltip({
11
+ content,
12
+ children,
13
+ position = "bottom",
14
+ delay = 200,
15
+ }: TooltipProps) {
16
+ const [isVisible, setIsVisible] = useState(false);
17
+ const [timeoutId, setTimeoutId] = useState<NodeJS.Timeout | null>(null);
18
+
19
+ const showTooltip = () => {
20
+ const id = setTimeout(() => setIsVisible(true), delay);
21
+ setTimeoutId(id);
22
+ };
23
+
24
+ const hideTooltip = () => {
25
+ if (timeoutId) clearTimeout(timeoutId);
26
+ setIsVisible(false);
27
+ };
28
+
29
+ const positionClasses = {
30
+ top: "bottom-full left-1/2 -translate-x-1/2 mb-2",
31
+ bottom: "top-full left-1/2 -translate-x-1/2 mt-2",
32
+ left: "right-full top-1/2 -translate-y-1/2 mr-2",
33
+ right: "left-full top-1/2 -translate-y-1/2 ml-2",
34
+ };
35
+
36
+ return (
37
+ <div
38
+ className="relative inline-flex items-center"
39
+ onMouseEnter={showTooltip}
40
+ onMouseLeave={hideTooltip}
41
+ onFocus={showTooltip}
42
+ onBlur={hideTooltip}
43
+ >
44
+ {children}
45
+ {isVisible && (
46
+ <div
47
+ role="tooltip"
48
+ className={`absolute z-50 whitespace-nowrap bg-zinc-900 border border-zinc-700 text-zinc-100 text-xs px-2 py-1 rounded shadow-lg transition-opacity duration-200 animate-in fade-in zoom-in-95 ${positionClasses[position]}`}
49
+ >
50
+ {content}
51
+ {/* Arrow */}
52
+ <div
53
+ className={`absolute w-2 h-2 bg-zinc-900 border-zinc-700 transform rotate-45 ${
54
+ position === "top"
55
+ ? "bottom-[-5px] left-1/2 -translate-x-1/2 border-b border-r"
56
+ : position === "bottom"
57
+ ? "top-[-5px] left-1/2 -translate-x-1/2 border-t border-l"
58
+ : position === "left"
59
+ ? "right-[-5px] top-1/2 -translate-y-1/2 border-t border-r"
60
+ : "left-[-5px] top-1/2 -translate-y-1/2 border-b border-l"
61
+ }`}
62
+ ></div>
63
+ </div>
64
+ )}
65
+ </div>
66
+ );
67
+ }
@@ -0,0 +1,20 @@
1
+ export const chatShortcuts = [
2
+ {
3
+ label: "🧠 Tengo una idea mediocre, hacela brillante",
4
+ prompt: "Tengo una idea mediocre, hacela brillante",
5
+ },
6
+ {
7
+ label: "🗓️ Organizá mi semana antes de que me ahogue en caos",
8
+ prompt: "Organizá mi semana antes de que me ahogue en caos",
9
+ },
10
+ {
11
+ label: "🧪 Opiná sobre algo que todavía no existe",
12
+ prompt:
13
+ "Opiná sobre algo que todavía no existe y decime por qué va a fallar",
14
+ },
15
+ {
16
+ label: "❓ Explicame esto como si tuviera 3 neuronas",
17
+ prompt:
18
+ "Explicame esto como si tuviera 3 neuronas, sé cruel pero educativo",
19
+ },
20
+ ];
@@ -0,0 +1,6 @@
1
+ import { createClient } from "@supabase/supabase-js";
2
+
3
+ export const supabase = createClient(
4
+ import.meta.env.VITE_SUPABASE_URL || "",
5
+ import.meta.env.VITE_SUPABASE_KEY || ""
6
+ );
@@ -0,0 +1,3 @@
1
+ Neo-Y2K
2
+ Final
3
+ Check
@@ -0,0 +1,28 @@
1
+ export type AudioContextState = "suspended" | "running" | "closed";
2
+
3
+ export function useScouter() {
4
+ // WADI AUDIO PROTOCOL - DISABLED
5
+ // All audio logic has been stripped as per instructions.
6
+
7
+ // No-op functions to satisfy interface
8
+ const playScanSound = () => {};
9
+ const playAlertSound = () => {};
10
+ const playCrystallizeSound = () => {};
11
+ const playDeathSound = () => {};
12
+ const playYawnSound = () => {};
13
+ const initAmbientHum = () => {};
14
+ const setAmbientIntensity = (_level: "normal" | "high") => {
15
+ void _level;
16
+ };
17
+
18
+ return {
19
+ playScanSound,
20
+ playAlertSound,
21
+ playCrystallizeSound,
22
+ playDeathSound,
23
+ playYawnSound,
24
+ initAmbientHum,
25
+ setAmbientIntensity,
26
+ audioState: "suspended" as AudioContextState,
27
+ };
28
+ }
@@ -0,0 +1,24 @@
1
+ import { useEffect, useState } from "react";
2
+ import { useChatStore } from "../store/chatStore";
3
+
4
+ export const useStoreHydration = () => {
5
+ const [hydrated, setHydrated] = useState(() => {
6
+ return useChatStore.persist.hasHydrated();
7
+ });
8
+
9
+ useEffect(() => {
10
+ const unsub = useChatStore.persist.onFinishHydration(() => {
11
+ setHydrated(true);
12
+ });
13
+
14
+ if (useChatStore.persist.hasHydrated() && !hydrated) {
15
+ setTimeout(() => setHydrated(true), 0);
16
+ }
17
+
18
+ return () => {
19
+ if (unsub) unsub();
20
+ };
21
+ }, [hydrated]);
22
+
23
+ return hydrated;
24
+ };