Krish Patel
commited on
Commit
·
772d253
1
Parent(s):
b6597a0
added frontend
Browse files- nexus-frontend/.gitignore +24 -0
- nexus-frontend/README.md +50 -0
- nexus-frontend/eslint.config.js +28 -0
- nexus-frontend/guide.txt +185 -0
- nexus-frontend/index.html +13 -0
- nexus-frontend/package-lock.json +0 -0
- nexus-frontend/package.json +42 -0
- nexus-frontend/postcss.config.js +6 -0
- nexus-frontend/src/App.css +0 -0
- nexus-frontend/src/App.tsx +25 -0
- nexus-frontend/src/components/FactCheckStream.tsx +82 -0
- nexus-frontend/src/index.css +3 -0
- nexus-frontend/src/main.tsx +10 -0
- nexus-frontend/src/pages/Dashboard.tsx +602 -0
- nexus-frontend/src/pages/Home.css +91 -0
- nexus-frontend/src/pages/Home.tsx +100 -0
- nexus-frontend/src/pages/SignIn.tsx +5 -0
- nexus-frontend/src/pages/SignUp.tsx +5 -0
- nexus-frontend/src/pages/deepfake.tsx +177 -0
- nexus-frontend/src/vite-env.d.ts +1 -0
- nexus-frontend/tailwind.config.js +21 -0
- nexus-frontend/tsconfig.app.json +26 -0
- nexus-frontend/tsconfig.json +7 -0
- nexus-frontend/tsconfig.node.json +24 -0
- nexus-frontend/vite.config.ts +7 -0
nexus-frontend/.gitignore
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Logs
|
2 |
+
logs
|
3 |
+
*.log
|
4 |
+
npm-debug.log*
|
5 |
+
yarn-debug.log*
|
6 |
+
yarn-error.log*
|
7 |
+
pnpm-debug.log*
|
8 |
+
lerna-debug.log*
|
9 |
+
|
10 |
+
node_modules
|
11 |
+
dist
|
12 |
+
dist-ssr
|
13 |
+
*.local
|
14 |
+
|
15 |
+
# Editor directories and files
|
16 |
+
.vscode/*
|
17 |
+
!.vscode/extensions.json
|
18 |
+
.idea
|
19 |
+
.DS_Store
|
20 |
+
*.suo
|
21 |
+
*.ntvs*
|
22 |
+
*.njsproj
|
23 |
+
*.sln
|
24 |
+
*.sw?
|
nexus-frontend/README.md
ADDED
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# React + TypeScript + Vite
|
2 |
+
|
3 |
+
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
4 |
+
|
5 |
+
Currently, two official plugins are available:
|
6 |
+
|
7 |
+
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
|
8 |
+
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
|
9 |
+
|
10 |
+
## Expanding the ESLint configuration
|
11 |
+
|
12 |
+
If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
|
13 |
+
|
14 |
+
- Configure the top-level `parserOptions` property like this:
|
15 |
+
|
16 |
+
```js
|
17 |
+
export default tseslint.config({
|
18 |
+
languageOptions: {
|
19 |
+
// other options...
|
20 |
+
parserOptions: {
|
21 |
+
project: ['./tsconfig.node.json', './tsconfig.app.json'],
|
22 |
+
tsconfigRootDir: import.meta.dirname,
|
23 |
+
},
|
24 |
+
},
|
25 |
+
})
|
26 |
+
```
|
27 |
+
|
28 |
+
- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked`
|
29 |
+
- Optionally add `...tseslint.configs.stylisticTypeChecked`
|
30 |
+
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config:
|
31 |
+
|
32 |
+
```js
|
33 |
+
// eslint.config.js
|
34 |
+
import react from 'eslint-plugin-react'
|
35 |
+
|
36 |
+
export default tseslint.config({
|
37 |
+
// Set the react version
|
38 |
+
settings: { react: { version: '18.3' } },
|
39 |
+
plugins: {
|
40 |
+
// Add the react plugin
|
41 |
+
react,
|
42 |
+
},
|
43 |
+
rules: {
|
44 |
+
// other rules...
|
45 |
+
// Enable its recommended rules
|
46 |
+
...react.configs.recommended.rules,
|
47 |
+
...react.configs['jsx-runtime'].rules,
|
48 |
+
},
|
49 |
+
})
|
50 |
+
```
|
nexus-frontend/eslint.config.js
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import js from '@eslint/js'
|
2 |
+
import globals from 'globals'
|
3 |
+
import reactHooks from 'eslint-plugin-react-hooks'
|
4 |
+
import reactRefresh from 'eslint-plugin-react-refresh'
|
5 |
+
import tseslint from 'typescript-eslint'
|
6 |
+
|
7 |
+
export default tseslint.config(
|
8 |
+
{ ignores: ['dist'] },
|
9 |
+
{
|
10 |
+
extends: [js.configs.recommended, ...tseslint.configs.recommended],
|
11 |
+
files: ['**/*.{ts,tsx}'],
|
12 |
+
languageOptions: {
|
13 |
+
ecmaVersion: 2020,
|
14 |
+
globals: globals.browser,
|
15 |
+
},
|
16 |
+
plugins: {
|
17 |
+
'react-hooks': reactHooks,
|
18 |
+
'react-refresh': reactRefresh,
|
19 |
+
},
|
20 |
+
rules: {
|
21 |
+
...reactHooks.configs.recommended.rules,
|
22 |
+
'react-refresh/only-export-components': [
|
23 |
+
'warn',
|
24 |
+
{ allowConstantExport: true },
|
25 |
+
],
|
26 |
+
},
|
27 |
+
},
|
28 |
+
)
|
nexus-frontend/guide.txt
ADDED
@@ -0,0 +1,185 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
1. Website Implementation
|
2 |
+
Tech Stack:
|
3 |
+
|
4 |
+
Frontend Framework: React.js or Next.js
|
5 |
+
State Management: Redux or Zustand
|
6 |
+
Styling: Tailwind CSS
|
7 |
+
Real-time Processing: WebSockets (Socket.io)
|
8 |
+
Data Visualization: Recharts or D3.js
|
9 |
+
|
10 |
+
Implementation Approach:
|
11 |
+
|
12 |
+
Create a comprehensive dashboard with:
|
13 |
+
|
14 |
+
Real-time misinformation tracking
|
15 |
+
Confidence score visualizations
|
16 |
+
Fact-checking integration
|
17 |
+
Interactive knowledge graphs
|
18 |
+
User authentication system
|
19 |
+
|
20 |
+
|
21 |
+
Key Features:
|
22 |
+
|
23 |
+
Live broadcast content analysis
|
24 |
+
Instant claim verification
|
25 |
+
Source credibility tracking
|
26 |
+
Detailed impact analysis
|
27 |
+
Exportable reports
|
28 |
+
|
29 |
+
|
30 |
+
|
31 |
+
Deployment:
|
32 |
+
|
33 |
+
Cloud Platforms: AWS, Google Cloud
|
34 |
+
Containerization: Docker
|
35 |
+
Continuous Integration: GitHub Actions
|
36 |
+
|
37 |
+
2. Web Browser Extension
|
38 |
+
Tech Stack:
|
39 |
+
|
40 |
+
Core Technology: React.js
|
41 |
+
Language: TypeScript
|
42 |
+
Browser APIs: Chrome/Firefox Extension API
|
43 |
+
AI Integration: TensorFlow.js
|
44 |
+
State Management: Redux Toolkit
|
45 |
+
|
46 |
+
Implementation Approach:
|
47 |
+
|
48 |
+
Develop modular extension components:
|
49 |
+
|
50 |
+
Content script for webpage scanning
|
51 |
+
Background script for processing
|
52 |
+
Popup interface for settings/alerts
|
53 |
+
Overlay mechanism for fact-checking
|
54 |
+
|
55 |
+
|
56 |
+
Key Features:
|
57 |
+
|
58 |
+
Hover-based claim verification
|
59 |
+
Instant risk scoring
|
60 |
+
Minimal, non-intrusive design
|
61 |
+
Cross-site functionality
|
62 |
+
|
63 |
+
|
64 |
+
|
65 |
+
Deployment:
|
66 |
+
|
67 |
+
Chrome Web Store
|
68 |
+
Firefox Add-ons
|
69 |
+
Microsoft Edge Add-ons
|
70 |
+
|
71 |
+
3. Mobile App Extension
|
72 |
+
Tech Stack:
|
73 |
+
|
74 |
+
Framework: React Native
|
75 |
+
State Management: Redux
|
76 |
+
AI Integration: TensorFlow Lite
|
77 |
+
Backend: Node.js with Express
|
78 |
+
Mobile-specific APIs
|
79 |
+
|
80 |
+
Implementation Approach:
|
81 |
+
|
82 |
+
Create cross-platform mobile application
|
83 |
+
Features:
|
84 |
+
|
85 |
+
News feed fact-checking
|
86 |
+
Social media content analysis
|
87 |
+
Offline mode for saved checks
|
88 |
+
Push notifications for high-risk content
|
89 |
+
|
90 |
+
|
91 |
+
|
92 |
+
Deployment:
|
93 |
+
|
94 |
+
App Store
|
95 |
+
Google Play Store
|
96 |
+
Potential enterprise distribution
|
97 |
+
|
98 |
+
4. Alternative Frontend Ideas
|
99 |
+
4.1 Slack/Discord Bot
|
100 |
+
Tech Stack:
|
101 |
+
|
102 |
+
Node.js
|
103 |
+
Discord.js/Slack API
|
104 |
+
Machine Learning Integration
|
105 |
+
|
106 |
+
Approach:
|
107 |
+
|
108 |
+
Real-time message scanning
|
109 |
+
Automatic fact-checking in chat environments
|
110 |
+
Seamless integration with communication platforms
|
111 |
+
|
112 |
+
4.2 Telegram/WhatsApp Extension
|
113 |
+
Tech Stack:
|
114 |
+
|
115 |
+
Language: Python or Node.js
|
116 |
+
Telegram/WhatsApp API
|
117 |
+
Serverless Functions
|
118 |
+
|
119 |
+
Approach:
|
120 |
+
|
121 |
+
Message content analysis
|
122 |
+
Inline fact-checking
|
123 |
+
Support for group chats
|
124 |
+
Multilingual support
|
125 |
+
|
126 |
+
4.3 Desktop Application
|
127 |
+
Tech Stack:
|
128 |
+
|
129 |
+
Electron.js
|
130 |
+
React.js
|
131 |
+
Machine Learning Integration
|
132 |
+
Local data processing
|
133 |
+
|
134 |
+
Approach:
|
135 |
+
|
136 |
+
Standalone application
|
137 |
+
Background monitoring of browsing
|
138 |
+
Comprehensive reporting
|
139 |
+
Advanced visualization
|
140 |
+
|
141 |
+
Comparative Analysis
|
142 |
+
ApproachProsConsBest ForWebsiteComprehensive, AccessibleRequires active navigationDetailed analysis, Professional useWeb ExtensionNon-intrusive, InstantLimited complex UICasual browsing, Quick checksMobile AppPortable, Push NotificationsScreen limitationsOn-the-go fact-checkingSlack BotWorkplace IntegrationLimited to platformProfessional environmentsDesktop AppPowerful, Offline CapableResource-intensivePower users, Deep analysis
|
143 |
+
Recommended Implementation Strategy
|
144 |
+
|
145 |
+
Start with Web Extension
|
146 |
+
|
147 |
+
Fastest development
|
148 |
+
Immediate user value
|
149 |
+
Low barrier to entry
|
150 |
+
Iterative improvement
|
151 |
+
|
152 |
+
|
153 |
+
Parallel Development
|
154 |
+
|
155 |
+
Web Dashboard for comprehensive analysis
|
156 |
+
Mobile companion app
|
157 |
+
Bot integrations as stretch goals
|
158 |
+
|
159 |
+
|
160 |
+
|
161 |
+
Key Considerations
|
162 |
+
|
163 |
+
Privacy-first design
|
164 |
+
Lightweight AI models
|
165 |
+
Minimal data transmission
|
166 |
+
Cross-platform compatibility
|
167 |
+
User-friendly interfaces
|
168 |
+
Continuous model updating
|
169 |
+
Transparent verification process
|
170 |
+
|
171 |
+
Potential Challenges
|
172 |
+
|
173 |
+
Real-time processing speed
|
174 |
+
Accuracy of ML models
|
175 |
+
API integration complexities
|
176 |
+
User adoption and trust
|
177 |
+
Handling multilingual content
|
178 |
+
|
179 |
+
Long-term Vision
|
180 |
+
|
181 |
+
Expand to multiple platforms
|
182 |
+
Improve AI accuracy
|
183 |
+
Build community-driven verification
|
184 |
+
Educational components
|
185 |
+
Global misinformation tracking
|
nexus-frontend/index.html
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!doctype html>
|
2 |
+
<html lang="en">
|
3 |
+
<head>
|
4 |
+
<meta charset="UTF-8" />
|
5 |
+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
6 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
7 |
+
<title>NEXUS</title>
|
8 |
+
</head>
|
9 |
+
<body>
|
10 |
+
<div id="root"></div>
|
11 |
+
<script type="module" src="/src/main.tsx"></script>
|
12 |
+
</body>
|
13 |
+
</html>
|
nexus-frontend/package-lock.json
ADDED
The diff for this file is too large to render.
See raw diff
|
|
nexus-frontend/package.json
ADDED
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"name": "nexus-frontend",
|
3 |
+
"private": true,
|
4 |
+
"version": "0.0.0",
|
5 |
+
"type": "module",
|
6 |
+
"scripts": {
|
7 |
+
"dev": "vite",
|
8 |
+
"build": "tsc -b && vite build",
|
9 |
+
"lint": "eslint .",
|
10 |
+
"preview": "vite preview"
|
11 |
+
},
|
12 |
+
"dependencies": {
|
13 |
+
"@emotion/react": "^11.14.0",
|
14 |
+
"@emotion/styled": "^11.14.0",
|
15 |
+
"@fontsource/press-start-2p": "^5.1.0",
|
16 |
+
"@mui/icons-material": "^6.3.1",
|
17 |
+
"@mui/material": "^6.3.1",
|
18 |
+
"framer-motion": "^11.15.0",
|
19 |
+
"lucide-react": "^0.469.0",
|
20 |
+
"mui": "^0.0.1",
|
21 |
+
"react": "^18.3.1",
|
22 |
+
"react-dom": "^18.3.1",
|
23 |
+
"react-icons": "^5.4.0",
|
24 |
+
"react-router-dom": "^7.1.1"
|
25 |
+
},
|
26 |
+
"devDependencies": {
|
27 |
+
"@eslint/js": "^9.17.0",
|
28 |
+
"@types/react": "^18.3.18",
|
29 |
+
"@types/react-dom": "^18.3.5",
|
30 |
+
"@vitejs/plugin-react-swc": "^3.5.0",
|
31 |
+
"autoprefixer": "^10.4.20",
|
32 |
+
"eslint": "^9.17.0",
|
33 |
+
"eslint-plugin-react-hooks": "^5.0.0",
|
34 |
+
"eslint-plugin-react-refresh": "^0.4.16",
|
35 |
+
"globals": "^15.14.0",
|
36 |
+
"postcss": "^8.4.49",
|
37 |
+
"tailwindcss": "^3.4.17",
|
38 |
+
"typescript": "~5.6.2",
|
39 |
+
"typescript-eslint": "^8.18.2",
|
40 |
+
"vite": "^6.0.5"
|
41 |
+
}
|
42 |
+
}
|
nexus-frontend/postcss.config.js
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export default {
|
2 |
+
plugins: {
|
3 |
+
tailwindcss: {},
|
4 |
+
autoprefixer: {},
|
5 |
+
},
|
6 |
+
}
|
nexus-frontend/src/App.css
ADDED
File without changes
|
nexus-frontend/src/App.tsx
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
|
2 |
+
import DeepfakeDetection from './pages/deepfake';
|
3 |
+
import "./App.css";
|
4 |
+
|
5 |
+
// Import your page components
|
6 |
+
import Home from "./pages/Home";
|
7 |
+
import SignIn from "./pages/SignIn";
|
8 |
+
import SignUp from "./pages/SignUp";
|
9 |
+
import Dashboard from "./pages/Dashboard";
|
10 |
+
|
11 |
+
function App() {
|
12 |
+
return (
|
13 |
+
<Router>
|
14 |
+
<Routes>
|
15 |
+
<Route path="/" element={<Home />} />
|
16 |
+
<Route path="/login" element={<SignIn />} />
|
17 |
+
<Route path="/register" element={<SignUp />} />
|
18 |
+
<Route path="/dashboard" element={<Dashboard />} />
|
19 |
+
<Route path="/deepfake" element={<DeepfakeDetection />} />
|
20 |
+
</Routes>
|
21 |
+
</Router>
|
22 |
+
);
|
23 |
+
}
|
24 |
+
|
25 |
+
export default App;
|
nexus-frontend/src/components/FactCheckStream.tsx
ADDED
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { useEffect, useState } from "react";
|
2 |
+
|
3 |
+
interface CorrectionSource {
|
4 |
+
text: string;
|
5 |
+
url: string;
|
6 |
+
}
|
7 |
+
|
8 |
+
interface Claim {
|
9 |
+
claim: string;
|
10 |
+
verification_status: string;
|
11 |
+
}
|
12 |
+
|
13 |
+
interface MetaAnalysis {
|
14 |
+
information_ecosystem_impact: string;
|
15 |
+
recommended_actions: string[];
|
16 |
+
}
|
17 |
+
|
18 |
+
interface OverallAnalysis {
|
19 |
+
key_findings: string[];
|
20 |
+
patterns_identified: string[];
|
21 |
+
reliability_assessment: string;
|
22 |
+
truth_score: number;
|
23 |
+
}
|
24 |
+
|
25 |
+
interface DetailedAnalysis {
|
26 |
+
claim_analysis: Claim[];
|
27 |
+
meta_analysis: MetaAnalysis;
|
28 |
+
overall_analysis: OverallAnalysis;
|
29 |
+
}
|
30 |
+
|
31 |
+
interface Result {
|
32 |
+
timestamp: string;
|
33 |
+
original_text: string;
|
34 |
+
detailed_analysis: DetailedAnalysis;
|
35 |
+
correction_sources: {
|
36 |
+
[key: string]: {
|
37 |
+
[key: string]: CorrectionSource[];
|
38 |
+
};
|
39 |
+
};
|
40 |
+
url: string;
|
41 |
+
title: string;
|
42 |
+
summary: string;
|
43 |
+
}
|
44 |
+
|
45 |
+
interface ApiResponse {
|
46 |
+
result: Result;
|
47 |
+
}
|
48 |
+
|
49 |
+
const FactCheckStream = () => {
|
50 |
+
const [factChecks, setFactChecks] = useState<ApiResponse[] | []>([]);
|
51 |
+
|
52 |
+
useEffect(() => {
|
53 |
+
const ws = new WebSocket("http://localhost:8000/ws/factcheck-stream");
|
54 |
+
|
55 |
+
ws.onmessage = (event) => {
|
56 |
+
const result = JSON.parse(event.data);
|
57 |
+
console.log(result);
|
58 |
+
setFactChecks((prev) => [result, ...prev]);
|
59 |
+
};
|
60 |
+
|
61 |
+
ws.onerror = (error) => {
|
62 |
+
console.error("WebSocket error:", error);
|
63 |
+
};
|
64 |
+
|
65 |
+
return () => {
|
66 |
+
ws.close();
|
67 |
+
};
|
68 |
+
}, []);
|
69 |
+
|
70 |
+
return (
|
71 |
+
<div>
|
72 |
+
<h2>Live Fact Checks</h2>
|
73 |
+
{factChecks.map((check, index) => (
|
74 |
+
<div key={index} className="fact-check-card">
|
75 |
+
|
76 |
+
</div>
|
77 |
+
))}
|
78 |
+
</div>
|
79 |
+
);
|
80 |
+
};
|
81 |
+
|
82 |
+
export default FactCheckStream;
|
nexus-frontend/src/index.css
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
@tailwind base;
|
2 |
+
@tailwind components;
|
3 |
+
@tailwind utilities;
|
nexus-frontend/src/main.tsx
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { StrictMode } from 'react'
|
2 |
+
import { createRoot } from 'react-dom/client'
|
3 |
+
import './index.css'
|
4 |
+
import App from './App.tsx'
|
5 |
+
|
6 |
+
createRoot(document.getElementById('root')!).render(
|
7 |
+
<StrictMode>
|
8 |
+
<App />
|
9 |
+
</StrictMode>,
|
10 |
+
)
|
nexus-frontend/src/pages/Dashboard.tsx
ADDED
@@ -0,0 +1,602 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React, { useState, memo } from 'react';
|
2 |
+
import { FileCheck, Shield, Database, Newspaper, Search, CheckCircle, AlertTriangle, Lock, Unlock, MessageSquare } from 'lucide-react';
|
3 |
+
|
4 |
+
interface DetailedAnalysis {
|
5 |
+
gemini_analysis: {
|
6 |
+
predicted_classification: string;
|
7 |
+
confidence_score: string;
|
8 |
+
reasoning: string[];
|
9 |
+
};
|
10 |
+
text_classification: {
|
11 |
+
category: string;
|
12 |
+
writing_style: string;
|
13 |
+
target_audience: string;
|
14 |
+
content_type: string;
|
15 |
+
};
|
16 |
+
sentiment_analysis: {
|
17 |
+
primary_emotion: string;
|
18 |
+
emotional_intensity: string;
|
19 |
+
sensationalism_level: string;
|
20 |
+
bias_indicators: string[];
|
21 |
+
tone: {
|
22 |
+
formality: string;
|
23 |
+
style: string;
|
24 |
+
};
|
25 |
+
emotional_triggers: string[];
|
26 |
+
};
|
27 |
+
entity_recognition: {
|
28 |
+
source_credibility: string;
|
29 |
+
people: string[];
|
30 |
+
organizations: string[];
|
31 |
+
locations: string[];
|
32 |
+
dates: string[];
|
33 |
+
statistics: string[];
|
34 |
+
};
|
35 |
+
context: {
|
36 |
+
main_narrative: string;
|
37 |
+
supporting_elements: string[];
|
38 |
+
key_claims: string[];
|
39 |
+
narrative_structure: string;
|
40 |
+
};
|
41 |
+
fact_checking: {
|
42 |
+
verifiable_claims: string[];
|
43 |
+
evidence_present: string;
|
44 |
+
fact_check_score: string;
|
45 |
+
};
|
46 |
+
}
|
47 |
+
|
48 |
+
const AnimatedBackground = memo(() => {
|
49 |
+
const backgroundIcons = [
|
50 |
+
FileCheck, Shield, Database, Newspaper, Search, CheckCircle, AlertTriangle, Lock, Unlock, MessageSquare
|
51 |
+
];
|
52 |
+
|
53 |
+
return (
|
54 |
+
<div className="fixed inset-0 overflow-hidden">
|
55 |
+
<div className="absolute inset-0 transition-colors duration-300 bg-gradient-to-br from-gray-900 via-blue-900 to-gray-900 animate-gradient-dark" />
|
56 |
+
|
57 |
+
<div className="absolute inset-0 opacity-10">
|
58 |
+
<div className="absolute inset-0" style={{
|
59 |
+
backgroundImage: `linear-gradient(#ffffff20 1px, transparent 1px), linear-gradient(90deg, #ffffff20 1px, transparent 1px)`,
|
60 |
+
backgroundSize: '50px 50px',
|
61 |
+
animation: 'moveGrid 15s linear infinite'
|
62 |
+
}} />
|
63 |
+
</div>
|
64 |
+
|
65 |
+
<div className="absolute inset-0">
|
66 |
+
{backgroundIcons.map((Icon, index) => (
|
67 |
+
<div key={index}
|
68 |
+
className="absolute text-blue-400 opacity-10 animate-float-rotate"
|
69 |
+
style={{
|
70 |
+
left: `${(index * 20) % 100}%`,
|
71 |
+
top: `${(index * 25) % 100}%`,
|
72 |
+
animationDelay: `${index * 2}s`,
|
73 |
+
animationDuration: `${20 + index * 5}s`,
|
74 |
+
transform: `scale(${0.8 + (index % 3) * 0.2})`
|
75 |
+
}}>
|
76 |
+
<Icon size={48} />
|
77 |
+
</div>
|
78 |
+
))}
|
79 |
+
</div>
|
80 |
+
|
81 |
+
<div className="absolute inset-0">
|
82 |
+
{[...Array(5)].map((_, i) => (
|
83 |
+
<div key={`orb-${i}`}
|
84 |
+
className="absolute rounded-full bg-blue-400 blur-xl opacity-20 animate-pulse-float"
|
85 |
+
style={{
|
86 |
+
width: `${150 + i * 50}px`,
|
87 |
+
height: `${150 + i * 50}px`,
|
88 |
+
left: `${(i * 30) % 100}%`,
|
89 |
+
top: `${(i * 35) % 100}%`,
|
90 |
+
animationDelay: `${i * 2}s`,
|
91 |
+
animationDuration: `${10 + i * 2}s`
|
92 |
+
}} />
|
93 |
+
))}
|
94 |
+
</div>
|
95 |
+
|
96 |
+
<div className="absolute inset-0">
|
97 |
+
{[...Array(20)].map((_, i) => (
|
98 |
+
<div key={`particle-${i}`}
|
99 |
+
className="absolute w-1 h-1 rounded-full bg-blue-300 opacity-30 animate-particle"
|
100 |
+
style={{
|
101 |
+
left: `${Math.random() * 100}%`,
|
102 |
+
top: `${Math.random() * 100}%`,
|
103 |
+
animationDelay: `${Math.random() * 5}s`,
|
104 |
+
animationDuration: `${5 + Math.random() * 10}s`
|
105 |
+
}} />
|
106 |
+
))}
|
107 |
+
</div>
|
108 |
+
</div>
|
109 |
+
);
|
110 |
+
});
|
111 |
+
|
112 |
+
const Dashboard = () => {
|
113 |
+
const [inputText, setInputText] = useState('');
|
114 |
+
const [isProcessing, setIsProcessing] = useState(false);
|
115 |
+
const [result, setResult] = useState<{
|
116 |
+
prediction: string;
|
117 |
+
detailed_analysis: {
|
118 |
+
gemini_analysis: {
|
119 |
+
predicted_classification: string;
|
120 |
+
confidence_score: string;
|
121 |
+
reasoning: string[];
|
122 |
+
text_classification: {
|
123 |
+
category: string;
|
124 |
+
writing_style: string;
|
125 |
+
target_audience: string;
|
126 |
+
content_type: string;
|
127 |
+
};
|
128 |
+
sentiment_analysis: {
|
129 |
+
primary_emotion: string;
|
130 |
+
emotional_intensity: string;
|
131 |
+
sensationalism_level: string;
|
132 |
+
bias_indicators: string[];
|
133 |
+
tone: {
|
134 |
+
formality: string;
|
135 |
+
style: string;
|
136 |
+
};
|
137 |
+
emotional_triggers: string[];
|
138 |
+
};
|
139 |
+
entity_recognition: {
|
140 |
+
source_credibility: string;
|
141 |
+
people: string[];
|
142 |
+
organizations: string[];
|
143 |
+
locations: string[];
|
144 |
+
dates: string[];
|
145 |
+
statistics: string[];
|
146 |
+
};
|
147 |
+
context: {
|
148 |
+
main_narrative: string;
|
149 |
+
supporting_elements: string[];
|
150 |
+
key_claims: string[];
|
151 |
+
narrative_structure: string;
|
152 |
+
};
|
153 |
+
fact_checking: {
|
154 |
+
verifiable_claims: string[];
|
155 |
+
evidence_present: string;
|
156 |
+
fact_check_score: string;
|
157 |
+
};
|
158 |
+
};
|
159 |
+
};
|
160 |
+
} | null>(null);
|
161 |
+
const [showDetailedAnalysis, setShowDetailedAnalysis] = useState(false);
|
162 |
+
|
163 |
+
const processInput = async () => {
|
164 |
+
if (!inputText.trim()) return;
|
165 |
+
setIsProcessing(true);
|
166 |
+
setResult(null);
|
167 |
+
setShowDetailedAnalysis(false);
|
168 |
+
|
169 |
+
try {
|
170 |
+
const response = await fetch('http://localhost:8000/analyze', {
|
171 |
+
method: 'POST',
|
172 |
+
headers: {
|
173 |
+
'Content-Type': 'application/json',
|
174 |
+
},
|
175 |
+
body: JSON.stringify({ text: inputText }),
|
176 |
+
});
|
177 |
+
|
178 |
+
const data = await response.json();
|
179 |
+
setResult(data);
|
180 |
+
} catch (err) {
|
181 |
+
console.error('Analysis error:', err);
|
182 |
+
} finally {
|
183 |
+
setIsProcessing(false);
|
184 |
+
}
|
185 |
+
};
|
186 |
+
|
187 |
+
return (
|
188 |
+
<div className="min-h-screen relative overflow-hidden">
|
189 |
+
<AnimatedBackground />
|
190 |
+
<div className="relative z-10 max-w-3xl mx-auto p-6">
|
191 |
+
<div className="bg-gray-800/80 rounded-lg p-6 backdrop-blur-lg">
|
192 |
+
<div className="mb-6">
|
193 |
+
<h1 className="text-3xl font-bold text-white">
|
194 |
+
TruthTell Analysis
|
195 |
+
</h1>
|
196 |
+
</div>
|
197 |
+
|
198 |
+
<div className="space-y-6">
|
199 |
+
<div className="relative">
|
200 |
+
<textarea
|
201 |
+
value={inputText}
|
202 |
+
onChange={(e) => setInputText(e.target.value)}
|
203 |
+
className="w-full h-40 p-4 rounded-xl bg-gray-700 text-white placeholder-gray-400 focus:outline-none"
|
204 |
+
placeholder="Enter text to analyze..."
|
205 |
+
/>
|
206 |
+
<button
|
207 |
+
onClick={processInput}
|
208 |
+
disabled={isProcessing || !inputText.trim()}
|
209 |
+
className="absolute bottom-4 right-4 px-6 py-2 rounded-lg bg-blue-500 hover:bg-blue-600 text-white disabled:opacity-50"
|
210 |
+
>
|
211 |
+
Analyze
|
212 |
+
</button>
|
213 |
+
</div>
|
214 |
+
|
215 |
+
{isProcessing && (
|
216 |
+
<div className="text-center p-8">
|
217 |
+
<div className="text-blue-400">Processing...</div>
|
218 |
+
</div>
|
219 |
+
)}
|
220 |
+
|
221 |
+
{result && (
|
222 |
+
<div className="bg-gray-700/80 rounded-xl p-6">
|
223 |
+
<div className="text-center mb-4">
|
224 |
+
<h2 className="text-2xl font-bold text-white capitalize">
|
225 |
+
{result.prediction}
|
226 |
+
</h2>
|
227 |
+
</div>
|
228 |
+
|
229 |
+
<div className="space-y-4">
|
230 |
+
<div>
|
231 |
+
<div className="flex justify-between text-sm text-blue-300 mb-2">
|
232 |
+
<span>Confidence</span>
|
233 |
+
<span>{result.detailed_analysis.gemini_analysis.confidence_score}%</span>
|
234 |
+
</div>
|
235 |
+
<div className="h-2 bg-blue-900 rounded">
|
236 |
+
<div
|
237 |
+
style={{ width: `${result.detailed_analysis.gemini_analysis.confidence_score}%` }}
|
238 |
+
className="h-full bg-blue-500 rounded"
|
239 |
+
/>
|
240 |
+
</div>
|
241 |
+
</div>
|
242 |
+
|
243 |
+
<button
|
244 |
+
onClick={() => setShowDetailedAnalysis(!showDetailedAnalysis)}
|
245 |
+
className="w-full mt-4 px-6 py-2 rounded-lg bg-blue-600 hover:bg-blue-700 text-white transition-colors"
|
246 |
+
>
|
247 |
+
{showDetailedAnalysis ? 'Hide Detailed Analysis' : 'View Detailed Analysis'}
|
248 |
+
</button>
|
249 |
+
|
250 |
+
{showDetailedAnalysis && (
|
251 |
+
<div className="mt-4 p-4 bg-gray-800 rounded-lg">
|
252 |
+
<h3 className="text-xl font-semibold text-white mb-3">Detailed Analysis</h3>
|
253 |
+
<div className="space-y-4 text-gray-300">
|
254 |
+
{result?.detailed_analysis?.text_classification && (
|
255 |
+
<div>
|
256 |
+
<h4 className="font-semibold text-blue-300">Text Classification</h4>
|
257 |
+
<p>Category: {result.detailed_analysis.text_classification.category || 'N/A'}</p>
|
258 |
+
<p>Writing Style: {result.detailed_analysis.text_classification.writing_style || 'N/A'}</p>
|
259 |
+
<p>Target Audience: {result.detailed_analysis.text_classification.target_audience || 'N/A'}</p>
|
260 |
+
<p>Content Type: {result.detailed_analysis.text_classification.content_type || 'N/A'}</p>
|
261 |
+
</div>
|
262 |
+
)}
|
263 |
+
|
264 |
+
{result?.detailed_analysis?.sentiment_analysis && (
|
265 |
+
<div>
|
266 |
+
<h4 className="font-semibold text-blue-300">Sentiment Analysis</h4>
|
267 |
+
<p>Primary Emotion: {result.detailed_analysis.sentiment_analysis.primary_emotion || 'N/A'}</p>
|
268 |
+
<p>Emotional Intensity: {result.detailed_analysis.sentiment_analysis.emotional_intensity || 'N/A'}</p>
|
269 |
+
<p>Sensationalism Level: {result.detailed_analysis.sentiment_analysis.sensationalism_level || 'N/A'}</p>
|
270 |
+
<p>Tone: {result.detailed_analysis.sentiment_analysis.tone?.formality || 'N/A'},
|
271 |
+
{result.detailed_analysis.sentiment_analysis.tone?.style || 'N/A'}</p>
|
272 |
+
</div>
|
273 |
+
)}
|
274 |
+
|
275 |
+
{result?.detailed_analysis?.entity_recognition && (
|
276 |
+
<div>
|
277 |
+
<h4 className="font-semibold text-blue-300">Entity Recognition</h4>
|
278 |
+
<p>Source Credibility: {result.detailed_analysis.entity_recognition.source_credibility || 'N/A'}</p>
|
279 |
+
<p>People: {result.detailed_analysis.entity_recognition.people?.join(', ') || 'None'}</p>
|
280 |
+
<p>Organizations: {result.detailed_analysis.entity_recognition.organizations?.join(', ') || 'None'}</p>
|
281 |
+
<p>Locations: {result.detailed_analysis.entity_recognition.locations?.join(', ') || 'None'}</p>
|
282 |
+
</div>
|
283 |
+
)}
|
284 |
+
|
285 |
+
{result?.detailed_analysis?.fact_checking && (
|
286 |
+
<div>
|
287 |
+
<h4 className="font-semibold text-blue-300">Fact Checking</h4>
|
288 |
+
<p>Evidence Present: {result.detailed_analysis.fact_checking.evidence_present || 'N/A'}</p>
|
289 |
+
<p>Fact Check Score: {result.detailed_analysis.fact_checking.fact_check_score || 'N/A'}</p>
|
290 |
+
<p>Verifiable Claims: {result.detailed_analysis.fact_checking.verifiable_claims?.join(', ') || 'None'}</p>
|
291 |
+
</div>
|
292 |
+
)}
|
293 |
+
</div>
|
294 |
+
</div>
|
295 |
+
)}
|
296 |
+
|
297 |
+
|
298 |
+
</div>
|
299 |
+
</div>
|
300 |
+
)}
|
301 |
+
</div>
|
302 |
+
</div>
|
303 |
+
</div>
|
304 |
+
{/* Keep the existing style tag */}
|
305 |
+
<style dangerouslySetInnerHTML={{ __html: `
|
306 |
+
@keyframes floatRotate {
|
307 |
+
0% { transform: translate(0, 0) rotate(0deg); }
|
308 |
+
50% { transform: translate(20px, -20px) rotate(180deg); }
|
309 |
+
100% { transform: translate(0, 0) rotate(360deg); }
|
310 |
+
}
|
311 |
+
@keyframes pulseFloat {
|
312 |
+
0% { transform: translate(0, 0) scale(1); opacity: 0.2; }
|
313 |
+
50% { transform: translate(30px, -30px) scale(1.2); opacity: 0.3; }
|
314 |
+
100% { transform: translate(0, 0) scale(1); opacity: 0.2; }
|
315 |
+
}
|
316 |
+
@keyframes moveGrid {
|
317 |
+
0% { transform: translate(0, 0); }
|
318 |
+
100% { transform: translate(50px, 50px); }
|
319 |
+
}
|
320 |
+
@keyframes particle {
|
321 |
+
0% { transform: translate(0, 0) scale(1); opacity: 0.3; }
|
322 |
+
50% { transform: translate(100px, -100px) scale(2); opacity: 0.6; }
|
323 |
+
100% { transform: translate(0, 0) scale(1); opacity: 0
|
324 |
+
|
325 |
+
}
|
326 |
+
@keyframes gradientAnimation {
|
327 |
+
0% { background-position: 0% 50%; }
|
328 |
+
50% { background-position: 100% 50%; }
|
329 |
+
100% { background-position: 0% 50%; }
|
330 |
+
}
|
331 |
+
.animate-float-rotate { animation: floatRotate 20s infinite linear; }
|
332 |
+
.animate-pulse-float { animation: pulseFloat 10s infinite ease-in-out; }
|
333 |
+
.animate-particle { animation: particle 8s infinite ease-in-out; }
|
334 |
+
.animate-gradient-dark { background-size: 400% 400%; animation: gradientAnimation 15s ease infinite; }
|
335 |
+
`}} />
|
336 |
+
</div>
|
337 |
+
);
|
338 |
+
};
|
339 |
+
|
340 |
+
export default Dashboard;
|
341 |
+
|
342 |
+
// import React, { useState, memo } from 'react';
|
343 |
+
// import { FileCheck, Shield, Database, Newspaper, Search, CheckCircle, AlertTriangle, Lock, Unlock, MessageSquare } from 'lucide-react';
|
344 |
+
|
345 |
+
// const AnimatedBackground = memo(() => {
|
346 |
+
// const backgroundIcons = [
|
347 |
+
// FileCheck, Shield, Database, Newspaper, Search, CheckCircle, AlertTriangle, Lock, Unlock, MessageSquare
|
348 |
+
// ];
|
349 |
+
|
350 |
+
// return (
|
351 |
+
// <div className="fixed inset-0 overflow-hidden">
|
352 |
+
// <div className="absolute inset-0 transition-colors duration-300 bg-gradient-to-br from-gray-900 via-blue-900 to-gray-900 animate-gradient-dark" />
|
353 |
+
|
354 |
+
// <div className="absolute inset-0 opacity-10">
|
355 |
+
// <div className="absolute inset-0" style={{
|
356 |
+
// backgroundImage: `linear-gradient(#ffffff20 1px, transparent 1px), linear-gradient(90deg, #ffffff20 1px, transparent 1px)`,
|
357 |
+
// backgroundSize: '50px 50px',
|
358 |
+
// animation: 'moveGrid 15s linear infinite'
|
359 |
+
// }} />
|
360 |
+
// </div>
|
361 |
+
|
362 |
+
// <div className="absolute inset-0">
|
363 |
+
// {backgroundIcons.map((Icon, index) => (
|
364 |
+
// <div key={index}
|
365 |
+
// className="absolute text-blue-400 opacity-10 animate-float-rotate"
|
366 |
+
// style={{
|
367 |
+
// left: `${(index * 20) % 100}%`,
|
368 |
+
// top: `${(index * 25) % 100}%`,
|
369 |
+
// animationDelay: `${index * 2}s`,
|
370 |
+
// animationDuration: `${20 + index * 5}s`,
|
371 |
+
// transform: `scale(${0.8 + (index % 3) * 0.2})`
|
372 |
+
// }}>
|
373 |
+
// <Icon size={48} />
|
374 |
+
// </div>
|
375 |
+
// ))}
|
376 |
+
// </div>
|
377 |
+
|
378 |
+
// <div className="absolute inset-0">
|
379 |
+
// {[...Array(5)].map((_, i) => (
|
380 |
+
// <div key={`orb-${i}`}
|
381 |
+
// className="absolute rounded-full bg-blue-400 blur-xl opacity-20 animate-pulse-float"
|
382 |
+
// style={{
|
383 |
+
// width: `${150 + i * 50}px`,
|
384 |
+
// height: `${150 + i * 50}px`,
|
385 |
+
// left: `${(i * 30) % 100}%`,
|
386 |
+
// top: `${(i * 35) % 100}%`,
|
387 |
+
// animationDelay: `${i * 2}s`,
|
388 |
+
// animationDuration: `${10 + i * 2}s`
|
389 |
+
// }} />
|
390 |
+
// ))}
|
391 |
+
// </div>
|
392 |
+
|
393 |
+
// <div className="absolute inset-0">
|
394 |
+
// {[...Array(20)].map((_, i) => (
|
395 |
+
// <div key={`particle-${i}`}
|
396 |
+
// className="absolute w-1 h-1 rounded-full bg-blue-300 opacity-30 animate-particle"
|
397 |
+
// style={{
|
398 |
+
// left: `${Math.random() * 100}%`,
|
399 |
+
// top: `${Math.random() * 100}%`,
|
400 |
+
// animationDelay: `${Math.random() * 5}s`,
|
401 |
+
// animationDuration: `${5 + Math.random() * 10}s`
|
402 |
+
// }} />
|
403 |
+
// ))}
|
404 |
+
// </div>
|
405 |
+
// </div>
|
406 |
+
// );
|
407 |
+
// });
|
408 |
+
|
409 |
+
// const Dashboard = () => {
|
410 |
+
// const [inputText, setInputText] = useState('');
|
411 |
+
// const [isProcessing, setIsProcessing] = useState(false);
|
412 |
+
// const [result, setResult] = useState<{
|
413 |
+
// detailed_analysis: {
|
414 |
+
// gemini_analysis: {
|
415 |
+
// predicted_classification: string;
|
416 |
+
// confidence_score: string;
|
417 |
+
// reasoning: string[];
|
418 |
+
// };
|
419 |
+
// text_classification: {
|
420 |
+
// category: string;
|
421 |
+
// writing_style: string;
|
422 |
+
// target_audience: string;
|
423 |
+
// content_type: string;
|
424 |
+
// };
|
425 |
+
// sentiment_analysis: {
|
426 |
+
// primary_emotion: string;
|
427 |
+
// emotional_intensity: string;
|
428 |
+
// sensationalism_level: string;
|
429 |
+
// bias_indicators: string[];
|
430 |
+
// tone: {
|
431 |
+
// formality: string;
|
432 |
+
// style: string;
|
433 |
+
// };
|
434 |
+
// emotional_triggers: string[];
|
435 |
+
// };
|
436 |
+
// fact_checking: {
|
437 |
+
// verifiable_claims: string[];
|
438 |
+
// evidence_present: string;
|
439 |
+
// fact_check_score: string;
|
440 |
+
// };
|
441 |
+
// };
|
442 |
+
// } | null>(null);
|
443 |
+
// const [showDetailedAnalysis, setShowDetailedAnalysis] = useState(false);
|
444 |
+
|
445 |
+
// const processInput = async () => {
|
446 |
+
// if (!inputText.trim()) return;
|
447 |
+
// setIsProcessing(true);
|
448 |
+
// setResult(null);
|
449 |
+
// setShowDetailedAnalysis(false);
|
450 |
+
|
451 |
+
// try {
|
452 |
+
// const response = await fetch('http://localhost:8000/analyze', {
|
453 |
+
// method: 'POST',
|
454 |
+
// headers: {
|
455 |
+
// 'Content-Type': 'application/json',
|
456 |
+
// },
|
457 |
+
// body: JSON.stringify({ text: inputText }),
|
458 |
+
// });
|
459 |
+
|
460 |
+
// const data = await response.json();
|
461 |
+
// setResult(data);
|
462 |
+
// } catch (err) {
|
463 |
+
// console.error('Analysis error:', err);
|
464 |
+
// } finally {
|
465 |
+
// setIsProcessing(false);
|
466 |
+
// }
|
467 |
+
// };
|
468 |
+
|
469 |
+
// return (
|
470 |
+
// <div className="min-h-screen relative overflow-hidden">
|
471 |
+
// <AnimatedBackground />
|
472 |
+
// <div className="relative z-10 max-w-3xl mx-auto p-6">
|
473 |
+
// <div className="bg-gray-800/80 rounded-lg p-6 backdrop-blur-lg">
|
474 |
+
// <div className="mb-6">
|
475 |
+
// <h1 className="text-3xl font-bold text-white">
|
476 |
+
// TruthTell Analysis
|
477 |
+
// </h1>
|
478 |
+
// </div>
|
479 |
+
|
480 |
+
// <div className="space-y-6">
|
481 |
+
// <div className="relative">
|
482 |
+
// <textarea
|
483 |
+
// value={inputText}
|
484 |
+
// onChange={(e) => setInputText(e.target.value)}
|
485 |
+
// className="w-full h-40 p-4 rounded-xl bg-gray-700 text-white placeholder-gray-400 focus:outline-none"
|
486 |
+
// placeholder="Enter text to analyze..."
|
487 |
+
// />
|
488 |
+
// <button
|
489 |
+
// onClick={processInput}
|
490 |
+
// disabled={isProcessing || !inputText.trim()}
|
491 |
+
// className="absolute bottom-4 right-4 px-6 py-2 rounded-lg bg-blue-500 hover:bg-blue-600 text-white disabled:opacity-50"
|
492 |
+
// >
|
493 |
+
// Analyze
|
494 |
+
// </button>
|
495 |
+
// </div>
|
496 |
+
|
497 |
+
// {isProcessing && (
|
498 |
+
// <div className="text-center p-8">
|
499 |
+
// <div className="text-blue-400">Processing...</div>
|
500 |
+
// </div>
|
501 |
+
// )}
|
502 |
+
|
503 |
+
// {result && (
|
504 |
+
// <div className="bg-gray-700/80 rounded-xl p-6">
|
505 |
+
// <div className="text-center mb-4">
|
506 |
+
// <h2 className="text-2xl font-bold text-white capitalize">
|
507 |
+
// {result.detailed_analysis.gemini_analysis.predicted_classification}
|
508 |
+
// </h2>
|
509 |
+
// </div>
|
510 |
+
|
511 |
+
// <div className="space-y-4">
|
512 |
+
// <div>
|
513 |
+
// <div className="flex justify-between text-sm text-blue-300 mb-2">
|
514 |
+
// <span>Confidence</span>
|
515 |
+
// <span>{result.detailed_analysis.gemini_analysis.confidence_score}%</span>
|
516 |
+
// </div>
|
517 |
+
// <div className="h-2 bg-blue-900 rounded">
|
518 |
+
// <div
|
519 |
+
// style={{ width: `${result.detailed_analysis.gemini_analysis.confidence_score}%` }}
|
520 |
+
// className="h-full bg-blue-500 rounded"
|
521 |
+
// />
|
522 |
+
// </div>
|
523 |
+
// </div>
|
524 |
+
|
525 |
+
// <button
|
526 |
+
// onClick={() => setShowDetailedAnalysis(!showDetailedAnalysis)}
|
527 |
+
// className="w-full mt-4 px-6 py-2 rounded-lg bg-blue-600 hover:bg-blue-700 text-white transition-colors"
|
528 |
+
// >
|
529 |
+
// {showDetailedAnalysis ? 'Hide Detailed Analysis' : 'View Detailed Analysis'}
|
530 |
+
// </button>
|
531 |
+
|
532 |
+
// {showDetailedAnalysis && (
|
533 |
+
// <div className="mt-4 p-4 bg-gray-800 rounded-lg">
|
534 |
+
// <h3 className="text-xl font-semibold text-white mb-3">Detailed Analysis</h3>
|
535 |
+
// <div className="space-y-4 text-gray-300">
|
536 |
+
// <div>
|
537 |
+
// <h4 className="font-semibold text-blue-300">Text Classification</h4>
|
538 |
+
// <p>Category: {result.detailed_analysis.text_classification.category || 'N/A'}</p>
|
539 |
+
// <p>Writing Style: {result.detailed_analysis.text_classification.writing_style || 'N/A'}</p>
|
540 |
+
// <p>Target Audience: {result.detailed_analysis.text_classification.target_audience || 'N/A'}</p>
|
541 |
+
// <p>Content Type: {result.detailed_analysis.text_classification.content_type || 'N/A'}</p>
|
542 |
+
// </div>
|
543 |
+
|
544 |
+
// <div>
|
545 |
+
// <h4 className="font-semibold text-blue-300">Sentiment Analysis</h4>
|
546 |
+
// <p>Primary Emotion: {result.detailed_analysis.sentiment_analysis.primary_emotion || 'N/A'}</p>
|
547 |
+
// <p>Emotional Intensity: {result.detailed_analysis.sentiment_analysis.emotional_intensity || 'N/A'}</p>
|
548 |
+
// <p>Sensationalism Level: {result.detailed_analysis.sentiment_analysis.sensationalism_level || 'N/A'}</p>
|
549 |
+
// <p>Tone: {result.detailed_analysis.sentiment_analysis.tone?.formality || 'N/A'},
|
550 |
+
// {result.detailed_analysis.sentiment_analysis.tone?.style || 'N/A'}</p>
|
551 |
+
// </div>
|
552 |
+
|
553 |
+
// <div>
|
554 |
+
// <h4 className="font-semibold text-blue-300">Fact Checking</h4>
|
555 |
+
// <p>Evidence Present: {result.detailed_analysis.fact_checking.evidence_present || 'N/A'}</p>
|
556 |
+
// <p>Fact Check Score: {result.detailed_analysis.fact_checking.fact_check_score || 'N/A'}</p>
|
557 |
+
// <p>Verifiable Claims: {result.detailed_analysis.fact_checking.verifiable_claims?.join(', ') || 'None'}</p>
|
558 |
+
// </div>
|
559 |
+
// </div>
|
560 |
+
// </div>
|
561 |
+
// )}
|
562 |
+
// </div>
|
563 |
+
// </div>
|
564 |
+
// )}
|
565 |
+
// </div>
|
566 |
+
// </div>
|
567 |
+
// </div>
|
568 |
+
// <style dangerouslySetInnerHTML={{ __html: `
|
569 |
+
// @keyframes floatRotate {
|
570 |
+
// 0% { transform: translate(0, 0) rotate(0deg); }
|
571 |
+
// 50% { transform: translate(20px, -20px) rotate(180deg); }
|
572 |
+
// 100% { transform: translate(0, 0) rotate(360deg); }
|
573 |
+
// }
|
574 |
+
// @keyframes pulseFloat {
|
575 |
+
// 0% { transform: translate(0, 0) scale(1); opacity: 0.2; }
|
576 |
+
// 50% { transform: translate(30px, -30px) scale(1.2); opacity: 0.3; }
|
577 |
+
// 100% { transform: translate(0, 0) scale(1); opacity: 0.2; }
|
578 |
+
// }
|
579 |
+
// @keyframes moveGrid {
|
580 |
+
// 0% { transform: translate(0, 0); }
|
581 |
+
// 100% { transform: translate(50px, 50px); }
|
582 |
+
// }
|
583 |
+
// @keyframes particle {
|
584 |
+
// 0% { transform: translate(0, 0) scale(1); opacity: 0.3; }
|
585 |
+
// 50% { transform: translate(100px, -100px) scale(2); opacity: 0.6; }
|
586 |
+
// 100% { transform: translate(0, 0) scale(1); opacity: 0.3; }
|
587 |
+
// }
|
588 |
+
// @keyframes gradientAnimation {
|
589 |
+
// 0% { background-position: 0% 50%; }
|
590 |
+
// 50% { background-position: 100% 50%; }
|
591 |
+
// 100% { background-position: 0% 50%; }
|
592 |
+
// }
|
593 |
+
// .animate-float-rotate { animation: floatRotate 20s infinite linear; }
|
594 |
+
// .animate-pulse-float { animation: pulseFloat 10s infinite ease-in-out; }
|
595 |
+
// .animate-particle { animation: particle 8s infinite ease-in-out; }
|
596 |
+
// .animate-gradient-dark { background-size: 400% 400%; animation: gradientAnimation 15s ease infinite; }
|
597 |
+
// `}} />
|
598 |
+
// </div>
|
599 |
+
// );
|
600 |
+
// };
|
601 |
+
|
602 |
+
// export default Dashboard;
|
nexus-frontend/src/pages/Home.css
ADDED
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.retro-theme {
|
2 |
+
background: #0a0a2a;
|
3 |
+
color: #00ff41;
|
4 |
+
font-family: "Press Start 2P", monospace;
|
5 |
+
}
|
6 |
+
|
7 |
+
.hero-section {
|
8 |
+
text-align: center;
|
9 |
+
margin: 4rem 0;
|
10 |
+
}
|
11 |
+
|
12 |
+
.glitch-text {
|
13 |
+
text-shadow: 2px 2px #ff00ff;
|
14 |
+
animation: glitch 1s infinite;
|
15 |
+
}
|
16 |
+
|
17 |
+
.subtitle {
|
18 |
+
color: #00ff41;
|
19 |
+
margin: 1rem 0;
|
20 |
+
}
|
21 |
+
|
22 |
+
.cta-button {
|
23 |
+
background: #ff00ff;
|
24 |
+
color: white;
|
25 |
+
border: none;
|
26 |
+
padding: 1rem 2rem;
|
27 |
+
font-size: 1.2rem;
|
28 |
+
cursor: pointer;
|
29 |
+
border-radius: 4px;
|
30 |
+
font-family: inherit;
|
31 |
+
}
|
32 |
+
|
33 |
+
.features-grid {
|
34 |
+
display: grid;
|
35 |
+
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
36 |
+
gap: 2rem;
|
37 |
+
margin: 4rem 0;
|
38 |
+
}
|
39 |
+
|
40 |
+
.feature-card {
|
41 |
+
background: rgba(255, 255, 255, 0.05);
|
42 |
+
padding: 2rem;
|
43 |
+
border-radius: 8px;
|
44 |
+
border: 1px solid #00ff41;
|
45 |
+
text-align: center;
|
46 |
+
}
|
47 |
+
|
48 |
+
.feature-icon {
|
49 |
+
font-size: 2.5rem;
|
50 |
+
margin-bottom: 1rem;
|
51 |
+
color: #ff00ff;
|
52 |
+
}
|
53 |
+
|
54 |
+
.tech-stack {
|
55 |
+
text-align: center;
|
56 |
+
margin: 4rem 0;
|
57 |
+
}
|
58 |
+
|
59 |
+
.tech-badges {
|
60 |
+
display: flex;
|
61 |
+
flex-wrap: wrap;
|
62 |
+
justify-content: center;
|
63 |
+
gap: 1rem;
|
64 |
+
margin-top: 2rem;
|
65 |
+
}
|
66 |
+
|
67 |
+
.badge {
|
68 |
+
background: #ff00ff;
|
69 |
+
color: white;
|
70 |
+
padding: 0.5rem 1rem;
|
71 |
+
border-radius: 20px;
|
72 |
+
font-size: 0.9rem;
|
73 |
+
}
|
74 |
+
|
75 |
+
@keyframes glitch {
|
76 |
+
0% {
|
77 |
+
text-shadow: 2px 2px #ff00ff;
|
78 |
+
}
|
79 |
+
25% {
|
80 |
+
text-shadow: -2px 2px #00ff41;
|
81 |
+
}
|
82 |
+
50% {
|
83 |
+
text-shadow: 2px -2px #ff00ff;
|
84 |
+
}
|
85 |
+
75% {
|
86 |
+
text-shadow: -2px -2px #00ff41;
|
87 |
+
}
|
88 |
+
100% {
|
89 |
+
text-shadow: 2px 2px #ff00ff;
|
90 |
+
}
|
91 |
+
}
|
nexus-frontend/src/pages/Home.tsx
ADDED
@@ -0,0 +1,100 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Link } from 'react-router-dom';
|
2 |
+
import { motion } from "framer-motion";
|
3 |
+
import { FaCheckCircle, FaBolt, FaDatabase, FaBrain } from "react-icons/fa";
|
4 |
+
import "./Home.css";
|
5 |
+
|
6 |
+
const Home = () => {
|
7 |
+
const features = [
|
8 |
+
{
|
9 |
+
icon: <FaBolt className="text-4xl text-fuchsia-500" />,
|
10 |
+
title: "Real-Time Detection",
|
11 |
+
description: "Instant fact-checking during live broadcasts",
|
12 |
+
},
|
13 |
+
{
|
14 |
+
icon: <FaBrain className="text-4xl text-fuchsia-500" />,
|
15 |
+
title: "AI-Powered Analysis",
|
16 |
+
description: "Advanced machine learning for accurate verification",
|
17 |
+
},
|
18 |
+
{
|
19 |
+
icon: <FaDatabase className="text-4xl text-fuchsia-500" />,
|
20 |
+
title: "Knowledge Graph",
|
21 |
+
description: "Comprehensive fact database with Neo4j",
|
22 |
+
},
|
23 |
+
{
|
24 |
+
icon: <FaCheckCircle className="text-4xl text-fuchsia-500" />,
|
25 |
+
title: "Truth Detection",
|
26 |
+
description: "Sophisticated NLP for misinformation detection",
|
27 |
+
},
|
28 |
+
];
|
29 |
+
|
30 |
+
return (
|
31 |
+
<div className="min-h-screen retro-theme p-8">
|
32 |
+
<motion.div
|
33 |
+
className="text-center my-16"
|
34 |
+
initial={{ opacity: 0, y: 20 }}
|
35 |
+
animate={{ opacity: 1, y: 0 }}
|
36 |
+
transition={{ duration: 0.8 }}
|
37 |
+
>
|
38 |
+
<h1 className="text-5xl md:text-8xl font-bold mb-4 glitch-text">
|
39 |
+
NEXUS OF TRUTH
|
40 |
+
</h1>
|
41 |
+
<p className="text-xl md:text-2xl mb-8">
|
42 |
+
Real-Time Misinformation Detection System
|
43 |
+
</p>
|
44 |
+
<Link to="/dashboard">
|
45 |
+
<motion.button
|
46 |
+
className="bg-fuchsia-600 text-white px-8 py-4 rounded-lg text-lg font-bold hover:bg-fuchsia-700 transition-colors"
|
47 |
+
whileHover={{ scale: 1.05 }}
|
48 |
+
whileTap={{ scale: 0.95 }}
|
49 |
+
>
|
50 |
+
Get Started
|
51 |
+
</motion.button>
|
52 |
+
</Link>
|
53 |
+
</motion.div>
|
54 |
+
|
55 |
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8 my-16">
|
56 |
+
{features.map((feature, index) => (
|
57 |
+
<motion.div
|
58 |
+
key={index}
|
59 |
+
className="p-6 rounded-xl border border-[#00ff41] bg-opacity-5 bg-white backdrop-blur-sm"
|
60 |
+
initial={{ opacity: 0, x: -20 }}
|
61 |
+
animate={{ opacity: 1, x: 0 }}
|
62 |
+
transition={{ delay: index * 0.2 }}
|
63 |
+
whileHover={{ scale: 1.05 }}
|
64 |
+
>
|
65 |
+
<div className="flex justify-center mb-4">{feature.icon}</div>
|
66 |
+
<h3 className="text-xl font-bold mb-2 text-center">
|
67 |
+
{feature.title}
|
68 |
+
</h3>
|
69 |
+
<p className="text-center text-sm">{feature.description}</p>
|
70 |
+
</motion.div>
|
71 |
+
))}
|
72 |
+
</div>
|
73 |
+
|
74 |
+
<motion.div
|
75 |
+
className="text-center my-16"
|
76 |
+
initial={{ opacity: 0 }}
|
77 |
+
animate={{ opacity: 1 }}
|
78 |
+
transition={{ delay: 1 }}
|
79 |
+
>
|
80 |
+
<h2 className="text-3xl font-bold mb-8">
|
81 |
+
Powered by Advanced Technology
|
82 |
+
</h2>
|
83 |
+
<div className="flex flex-wrap justify-center gap-4">
|
84 |
+
{["Python", "TensorFlow", "PyTorch", "BERT", "Kafka", "Neo4j"].map(
|
85 |
+
(tech, index) => (
|
86 |
+
<span
|
87 |
+
key={index}
|
88 |
+
className="px-4 py-2 bg-fuchsia-600 text-white rounded-full text-sm"
|
89 |
+
>
|
90 |
+
{tech}
|
91 |
+
</span>
|
92 |
+
)
|
93 |
+
)}
|
94 |
+
</div>
|
95 |
+
</motion.div>
|
96 |
+
</div>
|
97 |
+
);
|
98 |
+
};
|
99 |
+
|
100 |
+
export default Home;
|
nexus-frontend/src/pages/SignIn.tsx
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
const SignIn = () => {
|
2 |
+
return <div>SignIn</div>;
|
3 |
+
};
|
4 |
+
|
5 |
+
export default SignIn;
|
nexus-frontend/src/pages/SignUp.tsx
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
const SignUp = () => {
|
2 |
+
return <div>SignUp</div>;
|
3 |
+
};
|
4 |
+
|
5 |
+
export default SignUp;
|
nexus-frontend/src/pages/deepfake.tsx
ADDED
@@ -0,0 +1,177 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React, { useState, memo } from 'react';
|
2 |
+
import { FileCheck, Shield, Database, Newspaper, Search, CheckCircle, AlertTriangle, Lock, Unlock, MessageSquare } from 'lucide-react';
|
3 |
+
import CloudUploadIcon from '@mui/icons-material/CloudUpload';
|
4 |
+
|
5 |
+
const AnimatedBackground = memo(() => {
|
6 |
+
const backgroundIcons = [
|
7 |
+
FileCheck, Shield, Database, Newspaper, Search, CheckCircle, AlertTriangle, Lock, Unlock, MessageSquare
|
8 |
+
];
|
9 |
+
|
10 |
+
return (
|
11 |
+
<div className="fixed inset-0 overflow-hidden">
|
12 |
+
<div className="absolute inset-0 transition-colors duration-300 bg-gradient-to-br from-gray-900 via-blue-900 to-gray-900 animate-gradient-dark" />
|
13 |
+
|
14 |
+
{/* Grid Background */}
|
15 |
+
<div className="absolute inset-0 opacity-10">
|
16 |
+
<div className="absolute inset-0" style={{
|
17 |
+
backgroundImage: `linear-gradient(#ffffff20 1px, transparent 1px), linear-gradient(90deg, #ffffff20 1px, transparent 1px)`,
|
18 |
+
backgroundSize: '50px 50px',
|
19 |
+
animation: 'moveGrid 15s linear infinite'
|
20 |
+
}} />
|
21 |
+
</div>
|
22 |
+
|
23 |
+
{/* Floating Icons */}
|
24 |
+
<div className="absolute inset-0">
|
25 |
+
{backgroundIcons.map((Icon, index) => (
|
26 |
+
<div key={index}
|
27 |
+
className="absolute text-blue-400 opacity-10 animate-float-rotate"
|
28 |
+
style={{
|
29 |
+
left: `${(index * 20) % 100}%`,
|
30 |
+
top: `${(index * 25) % 100}%`,
|
31 |
+
animationDelay: `${index * 2}s`,
|
32 |
+
animationDuration: `${20 + index * 5}s`,
|
33 |
+
transform: `scale(${0.8 + (index % 3) * 0.2})`
|
34 |
+
}}>
|
35 |
+
<Icon size={48} />
|
36 |
+
</div>
|
37 |
+
))}
|
38 |
+
</div>
|
39 |
+
|
40 |
+
{/* Glowing Orbs */}
|
41 |
+
<div className="absolute inset-0">
|
42 |
+
{[...Array(5)].map((_, i) => (
|
43 |
+
<div key={`orb-${i}`}
|
44 |
+
className="absolute rounded-full bg-blue-400 blur-xl opacity-20 animate-pulse-float"
|
45 |
+
style={{
|
46 |
+
width: `${150 + i * 50}px`,
|
47 |
+
height: `${150 + i * 50}px`,
|
48 |
+
left: `${(i * 30) % 100}%`,
|
49 |
+
top: `${(i * 35) % 100}%`,
|
50 |
+
animationDelay: `${i * 2}s`,
|
51 |
+
animationDuration: `${10 + i * 2}s`
|
52 |
+
}} />
|
53 |
+
))}
|
54 |
+
</div>
|
55 |
+
</div>
|
56 |
+
);
|
57 |
+
});
|
58 |
+
|
59 |
+
const DeepfakeDetection = () => {
|
60 |
+
const [selectedImage, setSelectedImage] = useState(null);
|
61 |
+
const [result, setResult] = useState(null);
|
62 |
+
const [loading, setLoading] = useState(false);
|
63 |
+
|
64 |
+
const handleImageSelect = (event) => {
|
65 |
+
const file = event.target.files[0];
|
66 |
+
setSelectedImage(file);
|
67 |
+
};
|
68 |
+
|
69 |
+
const handleUpload = async () => {
|
70 |
+
if (!selectedImage) return;
|
71 |
+
setLoading(true);
|
72 |
+
const formData = new FormData();
|
73 |
+
formData.append('image', selectedImage);
|
74 |
+
|
75 |
+
try {
|
76 |
+
const response = await fetch('http://localhost:8000/detect-deepfake', {
|
77 |
+
method: 'POST',
|
78 |
+
body: formData,
|
79 |
+
});
|
80 |
+
const data = await response.json();
|
81 |
+
setResult(data);
|
82 |
+
} catch (error) {
|
83 |
+
console.error('Error:', error);
|
84 |
+
} finally {
|
85 |
+
setLoading(false);
|
86 |
+
}
|
87 |
+
};
|
88 |
+
|
89 |
+
return (
|
90 |
+
<div className="min-h-screen relative overflow-hidden">
|
91 |
+
<AnimatedBackground />
|
92 |
+
<div className="relative z-10 max-w-3xl mx-auto p-6">
|
93 |
+
<div className="bg-gray-800/80 rounded-lg p-6 backdrop-blur-lg">
|
94 |
+
<div className="mb-6">
|
95 |
+
<h1 className="text-3xl font-bold text-white">
|
96 |
+
Deepfake Detection
|
97 |
+
</h1>
|
98 |
+
</div>
|
99 |
+
|
100 |
+
<div className="space-y-6">
|
101 |
+
<div className="flex flex-col items-center">
|
102 |
+
<input
|
103 |
+
accept="image/*"
|
104 |
+
className="hidden"
|
105 |
+
id="image-upload"
|
106 |
+
type="file"
|
107 |
+
onChange={handleImageSelect}
|
108 |
+
/>
|
109 |
+
<label htmlFor="image-upload">
|
110 |
+
<div className="px-6 py-3 rounded-lg bg-blue-500 hover:bg-blue-600 text-white cursor-pointer flex items-center gap-2">
|
111 |
+
<CloudUploadIcon />
|
112 |
+
Upload Image
|
113 |
+
</div>
|
114 |
+
</label>
|
115 |
+
|
116 |
+
{selectedImage && (
|
117 |
+
<div className="mt-4 text-center">
|
118 |
+
<p className="text-blue-300">Selected: {selectedImage.name}</p>
|
119 |
+
<img
|
120 |
+
src={URL.createObjectURL(selectedImage)}
|
121 |
+
alt="Preview"
|
122 |
+
className="max-w-md mt-4 rounded-lg mx-auto"
|
123 |
+
/>
|
124 |
+
</div>
|
125 |
+
)}
|
126 |
+
|
127 |
+
<button
|
128 |
+
onClick={handleUpload}
|
129 |
+
disabled={!selectedImage || loading}
|
130 |
+
className="mt-4 px-6 py-2 rounded-lg bg-blue-500 hover:bg-blue-600 text-white disabled:opacity-50"
|
131 |
+
>
|
132 |
+
Analyze
|
133 |
+
</button>
|
134 |
+
</div>
|
135 |
+
|
136 |
+
{loading && (
|
137 |
+
<div className="text-center p-8">
|
138 |
+
<div className="text-blue-400">Processing...</div>
|
139 |
+
</div>
|
140 |
+
)}
|
141 |
+
|
142 |
+
{result && (
|
143 |
+
<div className="bg-gray-700/80 rounded-xl p-6 mt-4">
|
144 |
+
<h2 className="text-xl font-semibold text-white mb-3">Detection Results:</h2>
|
145 |
+
<pre className="text-gray-300 overflow-auto p-4 bg-gray-800 rounded-lg">
|
146 |
+
{JSON.stringify(result, null, 2)}
|
147 |
+
</pre>
|
148 |
+
</div>
|
149 |
+
)}
|
150 |
+
</div>
|
151 |
+
</div>
|
152 |
+
</div>
|
153 |
+
|
154 |
+
<style dangerouslySetInnerHTML={{ __html: `
|
155 |
+
@keyframes floatRotate {
|
156 |
+
0% { transform: translate(0, 0) rotate(0deg); }
|
157 |
+
50% { transform: translate(20px, -20px) rotate(180deg); }
|
158 |
+
100% { transform: translate(0, 0) rotate(360deg); }
|
159 |
+
}
|
160 |
+
@keyframes pulseFloat {
|
161 |
+
0% { transform: translate(0, 0) scale(1); opacity: 0.2; }
|
162 |
+
50% { transform: translate(30px, -30px) scale(1.2); opacity: 0.3; }
|
163 |
+
100% { transform: translate(0, 0) scale(1); opacity: 0.2; }
|
164 |
+
}
|
165 |
+
@keyframes moveGrid {
|
166 |
+
0% { transform: translate(0, 0); }
|
167 |
+
100% { transform: translate(50px, 50px); }
|
168 |
+
}
|
169 |
+
.animate-float-rotate { animation: floatRotate 20s infinite linear; }
|
170 |
+
.animate-pulse-float { animation: pulseFloat 10s infinite ease-in-out; }
|
171 |
+
.animate-gradient-dark { background-size: 400% 400%; animation: gradientAnimation 15s ease infinite; }
|
172 |
+
`}} />
|
173 |
+
</div>
|
174 |
+
);
|
175 |
+
};
|
176 |
+
|
177 |
+
export default DeepfakeDetection;
|
nexus-frontend/src/vite-env.d.ts
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
/// <reference types="vite/client" />
|
nexus-frontend/tailwind.config.js
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/** @type {import('tailwindcss').Config} */
|
2 |
+
module.exports = {
|
3 |
+
content: [
|
4 |
+
"./index.html",
|
5 |
+
"./src/**/*.{js,jsx,ts,tsx}",
|
6 |
+
],
|
7 |
+
theme: {
|
8 |
+
extend: {
|
9 |
+
keyframes: {
|
10 |
+
'scan-lines': {
|
11 |
+
'0%, 100%': { transform: 'translateY(-50%)' },
|
12 |
+
'50%': { transform: 'translateY(50%)' }
|
13 |
+
}
|
14 |
+
},
|
15 |
+
animation: {
|
16 |
+
'scan-lines': 'scan-lines 2s linear infinite'
|
17 |
+
}
|
18 |
+
},
|
19 |
+
},
|
20 |
+
plugins: [],
|
21 |
+
}
|
nexus-frontend/tsconfig.app.json
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"compilerOptions": {
|
3 |
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
4 |
+
"target": "ES2020",
|
5 |
+
"useDefineForClassFields": true,
|
6 |
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
7 |
+
"module": "ESNext",
|
8 |
+
"skipLibCheck": true,
|
9 |
+
|
10 |
+
/* Bundler mode */
|
11 |
+
"moduleResolution": "bundler",
|
12 |
+
"allowImportingTsExtensions": true,
|
13 |
+
"isolatedModules": true,
|
14 |
+
"moduleDetection": "force",
|
15 |
+
"noEmit": true,
|
16 |
+
"jsx": "react-jsx",
|
17 |
+
|
18 |
+
/* Linting */
|
19 |
+
"strict": true,
|
20 |
+
"noUnusedLocals": true,
|
21 |
+
"noUnusedParameters": true,
|
22 |
+
"noFallthroughCasesInSwitch": true,
|
23 |
+
"noUncheckedSideEffectImports": true
|
24 |
+
},
|
25 |
+
"include": ["src"]
|
26 |
+
}
|
nexus-frontend/tsconfig.json
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"files": [],
|
3 |
+
"references": [
|
4 |
+
{ "path": "./tsconfig.app.json" },
|
5 |
+
{ "path": "./tsconfig.node.json" }
|
6 |
+
]
|
7 |
+
}
|
nexus-frontend/tsconfig.node.json
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"compilerOptions": {
|
3 |
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
4 |
+
"target": "ES2022",
|
5 |
+
"lib": ["ES2023"],
|
6 |
+
"module": "ESNext",
|
7 |
+
"skipLibCheck": true,
|
8 |
+
|
9 |
+
/* Bundler mode */
|
10 |
+
"moduleResolution": "bundler",
|
11 |
+
"allowImportingTsExtensions": true,
|
12 |
+
"isolatedModules": true,
|
13 |
+
"moduleDetection": "force",
|
14 |
+
"noEmit": true,
|
15 |
+
|
16 |
+
/* Linting */
|
17 |
+
"strict": true,
|
18 |
+
"noUnusedLocals": true,
|
19 |
+
"noUnusedParameters": true,
|
20 |
+
"noFallthroughCasesInSwitch": true,
|
21 |
+
"noUncheckedSideEffectImports": true
|
22 |
+
},
|
23 |
+
"include": ["vite.config.ts"]
|
24 |
+
}
|
nexus-frontend/vite.config.ts
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { defineConfig } from 'vite'
|
2 |
+
import react from '@vitejs/plugin-react-swc'
|
3 |
+
|
4 |
+
// https://vite.dev/config/
|
5 |
+
export default defineConfig({
|
6 |
+
plugins: [react()],
|
7 |
+
})
|