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.
- package/.agent/workflows/auto_sync.md +19 -0
- package/.agent/workflows/kivo_pipeline.md +27 -0
- package/.devcontainer/devcontainer.json +27 -0
- package/.github/workflows/kivo-cicd.yml +45 -0
- package/.github/workflows/monorepo-ci.yml +51 -0
- package/.github/workflows/wadi-ci.yml +38 -0
- package/.husky/pre-commit +2 -0
- package/.prettierignore +12 -0
- package/.prettierrc +9 -0
- package/.vscode/settings.json +19 -0
- package/CHANGELOG.md +56 -0
- package/CODE_OF_CONDUCT.md +43 -0
- package/CONTRIBUTING.md +42 -0
- package/DEPLOY_GUIDE.md +143 -0
- package/GO_LIVE_CHECKLIST.md +41 -0
- package/GO_LIVE_COMPLETE_REPORT.md +58 -0
- package/GO_LIVE_VALIDATION.md +39 -0
- package/LEGAL.md +38 -0
- package/MANIFESTO.md +40 -0
- package/MANUAL.md +82 -0
- package/OPS_PLAN.md +90 -0
- package/README.md +126 -0
- package/RELEASE_NOTES.md +51 -0
- package/ROADMAP.md +51 -0
- package/api_listado.txt +2197 -0
- package/apps/api/WADI_PROTOCOL.md +52 -0
- package/apps/api/debug-brain.js +11 -0
- package/apps/api/package.json +21 -0
- package/apps/api/src/core/analisis.js +17 -0
- package/apps/api/src/core/errors.js +31 -0
- package/apps/api/src/core/logger.js +32 -0
- package/apps/api/src/core/prompt-kivo.js +62 -0
- package/apps/api/src/index.js +212 -0
- package/apps/api/src/layers/human_pattern/composeResponse.js +35 -0
- package/apps/api/src/layers/human_pattern/detectPattern.js +39 -0
- package/apps/api/src/layers/human_pattern/heuristics.js +28 -0
- package/apps/api/src/layers/human_pattern/index.js +28 -0
- package/apps/api/src/layers/human_pattern/socialMemory.js +35 -0
- package/apps/api/src/middleware/errorHandler.js +24 -0
- package/apps/api/src/middleware/rateLimiter.js +38 -0
- package/apps/api/src/middleware/requestLogger.js +31 -0
- package/apps/api/src/middleware/upload.js +21 -0
- package/apps/api/src/middleware/validation.js +70 -0
- package/apps/api/src/modules/data.js +25 -0
- package/apps/api/src/modules/marketing.js +54 -0
- package/apps/api/src/modules/projects.js +40 -0
- package/apps/api/src/openai.js +16 -0
- package/apps/api/src/preferences/index.js +20 -0
- package/apps/api/src/register_user.js +22 -0
- package/apps/api/src/routes/kivo.js +58 -0
- package/apps/api/src/routes/monitoring.js +55 -0
- package/apps/api/src/routes.js +656 -0
- package/apps/api/src/supabase.js +17 -0
- package/apps/api/src/tools/index.js +57 -0
- package/apps/api/src/wadi-brain.js +171 -0
- package/apps/api/supabase/migrations/20251218_audit_logs.sql +27 -0
- package/apps/api/supabase/migrations/v2-chat-persistence.sql +83 -0
- package/apps/api/supabase/migrations/v3-cascade-delete.sql +11 -0
- package/apps/api/supabase/migrations/v3-security-fix.sql +90 -0
- package/apps/api/supabase/migrations/v3-storage.sql +36 -0
- package/apps/api/supabase/migrations/v4-gamification.sql +83 -0
- package/apps/api/supabase/migrations/v5-smoke-index.sql +6 -0
- package/apps/api/supabase/migrations/v6-schema-integrity-fix.sql +98 -0
- package/apps/api/supabase/migrations/v7-performance-indexes.sql +5 -0
- package/apps/api/supabase/migrations/v7-security-hardening.sql +132 -0
- package/apps/api/supabase/migrations/v8-security-hardening.sql +79 -0
- package/apps/api/test_human_pattern.js +30 -0
- package/apps/api/test_human_pattern_vague.js +30 -0
- package/apps/api/test_output.txt +76 -0
- package/apps/api/test_vague.js +30 -0
- package/apps/api/test_vague_block.js +30 -0
- package/apps/api/test_wadi.js +31 -0
- package/apps/api/tsconfig.json +13 -0
- package/apps/frontend/.env.local +3 -0
- package/apps/frontend/README.md +73 -0
- package/apps/frontend/eslint.config.js +27 -0
- package/apps/frontend/index.html +49 -0
- package/apps/frontend/package.json +41 -0
- package/apps/frontend/postcss.config.cjs +6 -0
- package/apps/frontend/public/cursors/wadi-neutral.svg +1 -0
- package/apps/frontend/public/cursors/wadi-select.svg +1 -0
- package/apps/frontend/public/icon-192.svg +1 -0
- package/apps/frontend/public/icon-512.svg +1 -0
- package/apps/frontend/public/manifest.webmanifest +23 -0
- package/apps/frontend/public/sw.js +57 -0
- package/apps/frontend/public/vite.svg +1 -0
- package/apps/frontend/public/wadi.svg +5 -0
- package/apps/frontend/src/assets/react.svg +1 -0
- package/apps/frontend/src/components/AuthLoader.tsx +50 -0
- package/apps/frontend/src/components/ChatInput.tsx +272 -0
- package/apps/frontend/src/components/ChatInterface.tsx +202 -0
- package/apps/frontend/src/components/ErrorBoundary.tsx +52 -0
- package/apps/frontend/src/components/InputArea.tsx +201 -0
- package/apps/frontend/src/components/Layout.tsx +73 -0
- package/apps/frontend/src/components/MessageBubble.tsx +66 -0
- package/apps/frontend/src/components/OnboardingModal.tsx +108 -0
- package/apps/frontend/src/components/SettingsModal.tsx +187 -0
- package/apps/frontend/src/components/Sidebar.tsx +171 -0
- package/apps/frontend/src/components/WadiOnboarding.tsx +71 -0
- package/apps/frontend/src/components/auditor/AuditReport.tsx +166 -0
- package/apps/frontend/src/components/auditor/AuditorHeader.tsx +34 -0
- package/apps/frontend/src/components/auditor/ContextPanel.tsx +138 -0
- package/apps/frontend/src/components/auditor/DataDeconstructor.tsx +85 -0
- package/apps/frontend/src/components/auditor/DecisionWall.tsx +65 -0
- package/apps/frontend/src/components/auditor/Dropzone.tsx +137 -0
- package/apps/frontend/src/components/common/Button.tsx +97 -0
- package/apps/frontend/src/components/common/Card.tsx +43 -0
- package/apps/frontend/src/components/common/Input.tsx +73 -0
- package/apps/frontend/src/components/common/Modal.tsx +53 -0
- package/apps/frontend/src/components/ui/Button.tsx +68 -0
- package/apps/frontend/src/components/ui/Card.tsx +86 -0
- package/apps/frontend/src/components/ui/Input.tsx +28 -0
- package/apps/frontend/src/components/ui/LogItem.tsx +64 -0
- package/apps/frontend/src/components/ui/MondayButton.tsx +40 -0
- package/apps/frontend/src/components/ui/MondayCard.tsx +24 -0
- package/apps/frontend/src/components/ui/Scouter.tsx +208 -0
- package/apps/frontend/src/components/ui/TerminalInput.tsx +202 -0
- package/apps/frontend/src/components/ui/Tooltip.tsx +67 -0
- package/apps/frontend/src/config/chatShortcuts.ts +20 -0
- package/apps/frontend/src/config/supabase.ts +6 -0
- package/apps/frontend/src/final_status.txt +3 -0
- package/apps/frontend/src/hooks/useScouter.ts +28 -0
- package/apps/frontend/src/hooks/useStoreHydration.ts +24 -0
- package/apps/frontend/src/improvement_status.txt +5 -0
- package/apps/frontend/src/index.css +88 -0
- package/apps/frontend/src/main.tsx +62 -0
- package/apps/frontend/src/monday_status.txt +7 -0
- package/apps/frontend/src/pages/ChatPage.tsx +201 -0
- package/apps/frontend/src/pages/DashboardPage.tsx +375 -0
- package/apps/frontend/src/pages/IntroWadi.tsx +114 -0
- package/apps/frontend/src/pages/LandingPage.tsx +103 -0
- package/apps/frontend/src/pages/Login.tsx +190 -0
- package/apps/frontend/src/pages/PrivacyPage.tsx +213 -0
- package/apps/frontend/src/pages/ProjectDetail.tsx +80 -0
- package/apps/frontend/src/pages/Projects.tsx +247 -0
- package/apps/frontend/src/pages/TermsPage.tsx +202 -0
- package/apps/frontend/src/router.tsx +83 -0
- package/apps/frontend/src/store/authStore.ts +152 -0
- package/apps/frontend/src/store/chatStore.ts +837 -0
- package/apps/frontend/src/store/documentStore.ts +89 -0
- package/apps/frontend/src/store/projectsStore.ts +111 -0
- package/apps/frontend/src/store/runsStore.ts +98 -0
- package/apps/frontend/src/utils/api.ts +34 -0
- package/apps/frontend/src/vite-env.d.ts +7 -0
- package/apps/frontend/tailwind.config.cjs +32 -0
- package/apps/frontend/tsconfig.app.json +27 -0
- package/apps/frontend/tsconfig.json +7 -0
- package/apps/frontend/tsconfig.node.json +26 -0
- package/apps/frontend/vite.config.ts +25 -0
- package/apps/kivo/.firebase/hosting.d3d3.cache +11 -0
- package/apps/kivo/.firebaserc +5 -0
- package/apps/kivo/BRANDING_GUIDE.md +71 -0
- package/apps/kivo/DEPLOYMENT_READY.md +46 -0
- package/apps/kivo/DEPLOY_URL.md +12 -0
- package/apps/kivo/IMPLEMENTATION_REPORT.md +35 -0
- package/apps/kivo/IMPLEMENTATION_REPORT_FINAL.md +49 -0
- package/apps/kivo/PLAN_MOBILE_2.0.md +44 -0
- package/apps/kivo/PWA_VERIFICATION_GUIDE.md +77 -0
- package/apps/kivo/README.md +28 -0
- package/apps/kivo/REBUILD_REPORT.md +34 -0
- package/apps/kivo/UPGRADE_REPORT.md +35 -0
- package/apps/kivo/android/app/build.gradle +54 -0
- package/apps/kivo/android/app/capacitor.build.gradle +19 -0
- package/apps/kivo/android/app/proguard-rules.pro +21 -0
- package/apps/kivo/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java +26 -0
- package/apps/kivo/android/app/src/main/AndroidManifest.xml +35 -0
- package/apps/kivo/android/app/src/main/java/com/kivo/app/MainActivity.java +5 -0
- package/apps/kivo/android/app/src/main/res/drawable/ic_launcher_background.xml +170 -0
- package/apps/kivo/android/app/src/main/res/drawable/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-land-hdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-land-ldpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-land-mdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-land-night-hdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-land-night-ldpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-land-night-mdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-land-night-xhdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-land-night-xxhdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-land-night-xxxhdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-land-xhdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-land-xxhdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-land-xxxhdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-night/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-port-hdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-port-ldpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-port-mdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-port-night-hdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-port-night-ldpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-port-night-mdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-port-night-xhdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-port-night-xxhdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-port-night-xxxhdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-port-xhdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-port-xxhdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-port-xxxhdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +34 -0
- package/apps/kivo/android/app/src/main/res/layout/activity_main.xml +12 -0
- package/apps/kivo/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +9 -0
- package/apps/kivo/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +9 -0
- package/apps/kivo/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-hdpi/ic_launcher_background.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-ldpi/ic_launcher.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-ldpi/ic_launcher_background.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-ldpi/ic_launcher_foreground.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-ldpi/ic_launcher_round.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-mdpi/ic_launcher_background.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
- package/apps/kivo/android/app/src/main/res/values/ic_launcher_background.xml +4 -0
- package/apps/kivo/android/app/src/main/res/values/strings.xml +7 -0
- package/apps/kivo/android/app/src/main/res/values/styles.xml +22 -0
- package/apps/kivo/android/app/src/main/res/xml/file_paths.xml +5 -0
- package/apps/kivo/android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java +18 -0
- package/apps/kivo/android/build.gradle +29 -0
- package/apps/kivo/android/capacitor.settings.gradle +3 -0
- package/apps/kivo/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/apps/kivo/android/gradle/wrapper/gradle-wrapper.properties +7 -0
- package/apps/kivo/android/gradle.properties +22 -0
- package/apps/kivo/android/gradlew +252 -0
- package/apps/kivo/android/gradlew.bat +94 -0
- package/apps/kivo/android/settings.gradle +5 -0
- package/apps/kivo/android/variables.gradle +16 -0
- package/apps/kivo/assets/icon.png +0 -0
- package/apps/kivo/assets/splash.png +0 -0
- package/apps/kivo/capacitor.config.json +16 -0
- package/apps/kivo/firebase.json +6 -0
- package/apps/kivo/jest.config.js +4 -0
- package/apps/kivo/package.json +26 -0
- package/apps/kivo/tests_disabled/logic.test.js +34 -0
- package/apps/kivo/www/assets/icon-192.png +0 -0
- package/apps/kivo/www/assets/icon-512.png +0 -0
- package/apps/kivo/www/assets/kivo-icon.png +0 -0
- package/apps/kivo/www/assets/pop.mp3 +0 -0
- package/apps/kivo/www/favicon.ico +0 -0
- package/apps/kivo/www/firebase-config.js +75 -0
- package/apps/kivo/www/index.html +38 -0
- package/apps/kivo/www/manifest.json +29 -0
- package/apps/kivo/www/script.js +72 -0
- package/apps/kivo/www/style.css +82 -0
- package/apps/kivo/www/sw.js +46 -0
- package/apps/kivo-brain-api/.env.example +2 -0
- package/apps/kivo-brain-api/KIVO_BACKEND_SETUP.md +27 -0
- package/apps/kivo-brain-api/controllers/kivoController.js +31 -0
- package/apps/kivo-brain-api/index.js +24 -0
- package/apps/kivo-brain-api/package.json +17 -0
- package/apps/kivo-brain-api/routes/message.js +8 -0
- package/apps/kivo-brain-api/services/openaiService.js +64 -0
- package/apps/tests/wadi-tests.js +155 -0
- package/apps/wadi-brain/docs/README_REMOVE_OPTIONS.md +26 -0
- package/cli/commands/deploy.js +10 -0
- package/cli/commands/docs.js +16 -0
- package/cli/commands/explain.js +29 -0
- package/cli/commands/lint.js +14 -0
- package/cli/index.js +26 -0
- package/cli/package.json +12 -0
- package/docs/CNAME +1 -0
- package/docs/README.md +38 -0
- package/docs/USO.md +30 -0
- package/docs/index.html +46 -0
- package/eslint.config.js +101 -0
- package/final_validation.json +27 -0
- package/frontend_listado.txt +33387 -0
- package/listado.txt +12591 -0
- package/package.json +46 -0
- package/packages/logger/index.js +43 -0
- package/packages/logger/package.json +19 -0
- package/packages/logger/test-logger.js +7 -0
- package/packages_listado.txt +801 -0
- package/pnpm-workspace.yaml +6 -0
- package/reseteador_existencial.ps1 +40 -0
- package/scripts/bump-version.js +26 -0
- package/scripts/env.ps1 +13 -0
- package/scripts/setup_android.ps1 +25 -0
- package/scripts/setup_virtual_env.ps1 +77 -0
- package/scripts/smoke-test.js +84 -0
- package/scripts/trigger_render_deploy.ps1 +3 -0
- package/scripts/validate-release.js +34 -0
- package/temp_check.js +57 -0
- package/tsconfig.json +5 -0
- 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,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
|
+
};
|