Spaces:
Sleeping
Sleeping
lakshmisravya123 commited on
Commit ·
efceb0f
1
Parent(s): 91dfcf4
Major upgrade: comprehensive debate analysis
Browse files- backend/services/ai.js +39 -12
- backend/services/ai.js.backup +68 -0
- frontend/src/App.jsx +112 -23
- frontend/src/styles/global.css +62 -12
backend/services/ai.js
CHANGED
|
@@ -8,7 +8,7 @@ async function callAI(prompt) {
|
|
| 8 |
const res = await fetch('https://api.groq.com/openai/v1/chat/completions', {
|
| 9 |
method: 'POST',
|
| 10 |
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${GROQ_API_KEY}` },
|
| 11 |
-
body: JSON.stringify({ model: GROQ_MODEL, messages: [{ role: 'user', content: prompt }], temperature: 0.7 }),
|
| 12 |
});
|
| 13 |
if (res.ok) { const data = await res.json(); return data.choices[0].message.content; }
|
| 14 |
console.warn('Groq failed, falling back to Ollama...');
|
|
@@ -28,7 +28,7 @@ function parseJSON(text) {
|
|
| 28 |
}
|
| 29 |
|
| 30 |
async function settleArgument(topic, side1Name, side1Argument, side2Name, side2Argument) {
|
| 31 |
-
const text = await callAI(`You are "The Settler" - a
|
| 32 |
|
| 33 |
TOPIC: ${topic}
|
| 34 |
|
|
@@ -38,27 +38,54 @@ ${side1Argument}
|
|
| 38 |
${side2Name}'s ARGUMENT:
|
| 39 |
${side2Argument}
|
| 40 |
|
| 41 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 42 |
{
|
| 43 |
"topic": "<topic summary>",
|
| 44 |
"side1": {
|
| 45 |
"name": "${side1Name}",
|
| 46 |
"score": <1-100>,
|
| 47 |
-
"
|
| 48 |
-
"
|
| 49 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
},
|
| 51 |
"side2": {
|
| 52 |
"name": "${side2Name}",
|
| 53 |
"score": <1-100>,
|
| 54 |
-
"
|
| 55 |
-
"
|
| 56 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
},
|
| 58 |
"winner": "<name of winner or 'Draw'>",
|
| 59 |
-
"verdict": "<
|
| 60 |
-
"commonGround": "<
|
| 61 |
-
"plotTwist": "<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
}
|
| 63 |
|
| 64 |
Return ONLY JSON, no markdown.`);
|
|
|
|
| 8 |
const res = await fetch('https://api.groq.com/openai/v1/chat/completions', {
|
| 9 |
method: 'POST',
|
| 10 |
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${GROQ_API_KEY}` },
|
| 11 |
+
body: JSON.stringify({ model: GROQ_MODEL, messages: [{ role: 'user', content: prompt }], temperature: 0.7, max_tokens: 4096 }),
|
| 12 |
});
|
| 13 |
if (res.ok) { const data = await res.json(); return data.choices[0].message.content; }
|
| 14 |
console.warn('Groq failed, falling back to Ollama...');
|
|
|
|
| 28 |
}
|
| 29 |
|
| 30 |
async function settleArgument(topic, side1Name, side1Argument, side2Name, side2Argument) {
|
| 31 |
+
const text = await callAI(`You are "The Settler" - a world-class debate judge with expertise in logic, rhetoric, and critical thinking. You combine the analytical rigor of a philosophy professor with the wit of a late-night show host.
|
| 32 |
|
| 33 |
TOPIC: ${topic}
|
| 34 |
|
|
|
|
| 38 |
${side2Name}'s ARGUMENT:
|
| 39 |
${side2Argument}
|
| 40 |
|
| 41 |
+
Perform a DEEP analysis of both arguments. Check for:
|
| 42 |
+
- Logical fallacies (ad hominem, straw man, false dichotomy, appeal to authority, slippery slope, etc.)
|
| 43 |
+
- Evidence quality (anecdotal, statistical, expert opinion, common sense)
|
| 44 |
+
- Emotional vs rational reasoning balance
|
| 45 |
+
- Bias and assumptions
|
| 46 |
+
- Argument structure and coherence
|
| 47 |
+
|
| 48 |
+
Return ONLY valid JSON:
|
| 49 |
{
|
| 50 |
"topic": "<topic summary>",
|
| 51 |
"side1": {
|
| 52 |
"name": "${side1Name}",
|
| 53 |
"score": <1-100>,
|
| 54 |
+
"persuasivenessScore": <1-100>,
|
| 55 |
+
"factualScore": <1-100>,
|
| 56 |
+
"emotionalVsRational": { "emotional": <0-100>, "rational": <0-100> },
|
| 57 |
+
"strengths": ["<specific strength 1>", "<specific strength 2>", "<strength 3>"],
|
| 58 |
+
"weaknesses": ["<specific weakness 1>", "<weakness 2>"],
|
| 59 |
+
"fallacies": [{ "name": "<fallacy name>", "quote": "<the part that commits it>", "explanation": "<why it's a fallacy>" }],
|
| 60 |
+
"evidenceQuality": "<anecdotal/statistical/expert/mixed - with explanation>",
|
| 61 |
+
"devilsAdvocate": "<3 sentences arguing AGAINST this side>",
|
| 62 |
+
"steelMan": "<The strongest possible version of this argument rewritten in 2-3 sentences>",
|
| 63 |
+
"whatWouldChangeTheirMind": "<What evidence or argument would make this person reconsider?>"
|
| 64 |
},
|
| 65 |
"side2": {
|
| 66 |
"name": "${side2Name}",
|
| 67 |
"score": <1-100>,
|
| 68 |
+
"persuasivenessScore": <1-100>,
|
| 69 |
+
"factualScore": <1-100>,
|
| 70 |
+
"emotionalVsRational": { "emotional": <0-100>, "rational": <0-100> },
|
| 71 |
+
"strengths": ["<specific strength 1>", "<specific strength 2>", "<strength 3>"],
|
| 72 |
+
"weaknesses": ["<specific weakness 1>", "<weakness 2>"],
|
| 73 |
+
"fallacies": [{ "name": "<fallacy name>", "quote": "<the part that commits it>", "explanation": "<why it's a fallacy>" }],
|
| 74 |
+
"evidenceQuality": "<anecdotal/statistical/expert/mixed - with explanation>",
|
| 75 |
+
"devilsAdvocate": "<3 sentences arguing AGAINST this side>",
|
| 76 |
+
"steelMan": "<The strongest possible version of this argument rewritten in 2-3 sentences>",
|
| 77 |
+
"whatWouldChangeTheirMind": "<What evidence or argument would make this person reconsider?>"
|
| 78 |
},
|
| 79 |
"winner": "<name of winner or 'Draw'>",
|
| 80 |
+
"verdict": "<4-5 sentence witty final verdict with specific reasoning>",
|
| 81 |
+
"commonGround": "<2-3 sentences on where both sides actually agree>",
|
| 82 |
+
"plotTwist": "<A surprising perspective neither side considered>",
|
| 83 |
+
"biasCheck": "<Any shared biases or blind spots both arguers have>",
|
| 84 |
+
"compromise": "<A detailed middle-ground solution that could satisfy both sides>",
|
| 85 |
+
"debateTips": {
|
| 86 |
+
"side1Tip": "<Specific advice for ${side1Name} on how to argue better>",
|
| 87 |
+
"side2Tip": "<Specific advice for ${side2Name} on how to argue better>"
|
| 88 |
+
}
|
| 89 |
}
|
| 90 |
|
| 91 |
Return ONLY JSON, no markdown.`);
|
backend/services/ai.js.backup
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
const GROQ_API_KEY = process.env.GROQ_API_KEY;
|
| 2 |
+
const OLLAMA_URL = process.env.OLLAMA_URL || 'http://localhost:11434';
|
| 3 |
+
const GROQ_MODEL = process.env.GROQ_MODEL || 'llama-3.3-70b-versatile';
|
| 4 |
+
const OLLAMA_MODEL = process.env.OLLAMA_MODEL || 'llama3.2:3b';
|
| 5 |
+
|
| 6 |
+
async function callAI(prompt) {
|
| 7 |
+
if (GROQ_API_KEY) {
|
| 8 |
+
const res = await fetch('https://api.groq.com/openai/v1/chat/completions', {
|
| 9 |
+
method: 'POST',
|
| 10 |
+
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${GROQ_API_KEY}` },
|
| 11 |
+
body: JSON.stringify({ model: GROQ_MODEL, messages: [{ role: 'user', content: prompt }], temperature: 0.7 }),
|
| 12 |
+
});
|
| 13 |
+
if (res.ok) { const data = await res.json(); return data.choices[0].message.content; }
|
| 14 |
+
console.warn('Groq failed, falling back to Ollama...');
|
| 15 |
+
}
|
| 16 |
+
const res = await fetch(`${OLLAMA_URL}/api/generate`, {
|
| 17 |
+
method: 'POST',
|
| 18 |
+
headers: { 'Content-Type': 'application/json' },
|
| 19 |
+
body: JSON.stringify({ model: OLLAMA_MODEL, prompt, stream: false }),
|
| 20 |
+
});
|
| 21 |
+
if (!res.ok) throw new Error('Both Groq and Ollama failed.');
|
| 22 |
+
return (await res.json()).response;
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
function parseJSON(text) {
|
| 26 |
+
try { return JSON.parse(text.trim()); }
|
| 27 |
+
catch { const m = text.match(/\{[\s\S]*\}/); if (m) return JSON.parse(m[0]); throw new Error('Failed to parse AI response'); }
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
async function settleArgument(topic, side1Name, side1Argument, side2Name, side2Argument) {
|
| 31 |
+
const text = await callAI(`You are "The Settler" - a fair, witty, and brutally honest judge of arguments. You analyze both sides objectively.
|
| 32 |
+
|
| 33 |
+
TOPIC: ${topic}
|
| 34 |
+
|
| 35 |
+
${side1Name}'s ARGUMENT:
|
| 36 |
+
${side1Argument}
|
| 37 |
+
|
| 38 |
+
${side2Name}'s ARGUMENT:
|
| 39 |
+
${side2Argument}
|
| 40 |
+
|
| 41 |
+
Analyze both arguments thoroughly. Return ONLY valid JSON:
|
| 42 |
+
{
|
| 43 |
+
"topic": "<topic summary>",
|
| 44 |
+
"side1": {
|
| 45 |
+
"name": "${side1Name}",
|
| 46 |
+
"score": <1-100>,
|
| 47 |
+
"strengths": ["<s1>", "<s2>"],
|
| 48 |
+
"weaknesses": ["<w1>", "<w2>"],
|
| 49 |
+
"devilsAdvocate": "<2-3 sentences arguing AGAINST this side>"
|
| 50 |
+
},
|
| 51 |
+
"side2": {
|
| 52 |
+
"name": "${side2Name}",
|
| 53 |
+
"score": <1-100>,
|
| 54 |
+
"strengths": ["<s1>", "<s2>"],
|
| 55 |
+
"weaknesses": ["<w1>", "<w2>"],
|
| 56 |
+
"devilsAdvocate": "<2-3 sentences arguing AGAINST this side>"
|
| 57 |
+
},
|
| 58 |
+
"winner": "<name of winner or 'Draw'>",
|
| 59 |
+
"verdict": "<3-4 sentence witty final verdict explaining who won and why>",
|
| 60 |
+
"commonGround": "<1-2 sentences on where both sides actually agree>",
|
| 61 |
+
"plotTwist": "<a surprising perspective neither side considered>"
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
Return ONLY JSON, no markdown.`);
|
| 65 |
+
return parseJSON(text);
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
module.exports = { settleArgument };
|
frontend/src/App.jsx
CHANGED
|
@@ -1,6 +1,73 @@
|
|
| 1 |
import { useState } from 'react';
|
| 2 |
import { settleArgument } from './utils/api';
|
| 3 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
export default function App() {
|
| 5 |
const [topic, setTopic] = useState('');
|
| 6 |
const [side1Name, setSide1Name] = useState('');
|
|
@@ -44,7 +111,7 @@ export default function App() {
|
|
| 44 |
<div className="loading">
|
| 45 |
<div className="spinner"></div>
|
| 46 |
<p>The Settler is analyzing both sides...</p>
|
| 47 |
-
<p style={{ color: '#888', marginTop: '0.5rem', fontSize: '0.9rem' }}>
|
| 48 |
</div>
|
| 49 |
</div>
|
| 50 |
);
|
|
@@ -55,53 +122,75 @@ export default function App() {
|
|
| 55 |
<div className="app">
|
| 56 |
<h1><span>Argument Settler</span></h1>
|
| 57 |
<div className="results">
|
|
|
|
| 58 |
<div className="verdict-card">
|
| 59 |
<div style={{ fontSize: '0.9rem', color: '#888', marginBottom: '0.5rem' }}>THE WINNER IS</div>
|
| 60 |
<div className="winner-name">{result.winner === 'Draw' ? "It's a Draw!" : result.winner}</div>
|
| 61 |
<div className="verdict-text">{result.verdict}</div>
|
| 62 |
</div>
|
| 63 |
|
|
|
|
| 64 |
<div className="scores">
|
| 65 |
<div className="score-card left">
|
| 66 |
<h3>{result.side1?.name}</h3>
|
| 67 |
-
<div className="score-number">{result.side1?.score}
|
| 68 |
</div>
|
|
|
|
| 69 |
<div className="score-card right">
|
| 70 |
<h3>{result.side2?.name}</h3>
|
| 71 |
-
<div className="score-number">{result.side2?.score}
|
| 72 |
</div>
|
| 73 |
</div>
|
| 74 |
|
|
|
|
| 75 |
<div className="scores">
|
| 76 |
-
<
|
| 77 |
-
|
| 78 |
-
<ul>{result.side1?.strengths?.map((s, i) => <li key={i}>{s}</li>)}</ul>
|
| 79 |
-
<h4 style={{ marginTop: '1rem' }}>Weaknesses</h4>
|
| 80 |
-
<ul>{result.side1?.weaknesses?.map((w, i) => <li key={i}>{w}</li>)}</ul>
|
| 81 |
-
<h4 style={{ marginTop: '1rem', color: '#ff6b6b' }}>Devil's Advocate</h4>
|
| 82 |
-
<p style={{ color: '#bbb' }}>{result.side1?.devilsAdvocate}</p>
|
| 83 |
-
</div>
|
| 84 |
-
<div className="detail-section">
|
| 85 |
-
<h4>{result.side2?.name}'s Strengths</h4>
|
| 86 |
-
<ul>{result.side2?.strengths?.map((s, i) => <li key={i}>{s}</li>)}</ul>
|
| 87 |
-
<h4 style={{ marginTop: '1rem' }}>Weaknesses</h4>
|
| 88 |
-
<ul>{result.side2?.weaknesses?.map((w, i) => <li key={i}>{w}</li>)}</ul>
|
| 89 |
-
<h4 style={{ marginTop: '1rem', color: '#4ecdc4' }}>Devil's Advocate</h4>
|
| 90 |
-
<p style={{ color: '#bbb' }}>{result.side2?.devilsAdvocate}</p>
|
| 91 |
-
</div>
|
| 92 |
</div>
|
| 93 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
{result.commonGround && (
|
| 95 |
-
<div className="
|
| 96 |
<h4>Common Ground</h4>
|
| 97 |
-
<p
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 98 |
</div>
|
| 99 |
)}
|
| 100 |
|
|
|
|
| 101 |
{result.plotTwist && (
|
| 102 |
<div className="plot-twist">
|
| 103 |
<h4>Plot Twist</h4>
|
| 104 |
-
<p
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
</div>
|
| 106 |
)}
|
| 107 |
|
|
@@ -114,7 +203,7 @@ export default function App() {
|
|
| 114 |
return (
|
| 115 |
<div className="app">
|
| 116 |
<h1><span>Argument Settler</span></h1>
|
| 117 |
-
<p className="subtitle">AI
|
| 118 |
|
| 119 |
{error && <p style={{ color: '#ff6b6b', textAlign: 'center', marginBottom: '1rem' }}>{error}</p>}
|
| 120 |
|
|
|
|
| 1 |
import { useState } from 'react';
|
| 2 |
import { settleArgument } from './utils/api';
|
| 3 |
|
| 4 |
+
function ScoreBar({ label, value, color }) {
|
| 5 |
+
return (
|
| 6 |
+
<div className="score-bar-wrap">
|
| 7 |
+
<div className="score-bar-label">{label}</div>
|
| 8 |
+
<div className="score-bar-track">
|
| 9 |
+
<div className="score-bar-fill" style={{ width: `${value}%`, background: color }}></div>
|
| 10 |
+
</div>
|
| 11 |
+
<span className="score-bar-val">{value}</span>
|
| 12 |
+
</div>
|
| 13 |
+
);
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
function SidePanel({ side, color, accentClass }) {
|
| 17 |
+
return (
|
| 18 |
+
<div className={`detail-section ${accentClass}`}>
|
| 19 |
+
<h3 style={{ color }}>{side.name}</h3>
|
| 20 |
+
|
| 21 |
+
<div className="mini-scores">
|
| 22 |
+
<ScoreBar label="Overall" value={side.score} color={color} />
|
| 23 |
+
<ScoreBar label="Persuasiveness" value={side.persuasivenessScore} color={color} />
|
| 24 |
+
<ScoreBar label="Factual Accuracy" value={side.factualScore} color={color} />
|
| 25 |
+
</div>
|
| 26 |
+
|
| 27 |
+
{side.emotionalVsRational && (
|
| 28 |
+
<div className="emo-rational">
|
| 29 |
+
<span>Emotional {side.emotionalVsRational.emotional}%</span>
|
| 30 |
+
<div className="emo-bar">
|
| 31 |
+
<div className="emo-fill" style={{ width: `${side.emotionalVsRational.emotional}%` }}></div>
|
| 32 |
+
</div>
|
| 33 |
+
<span>Rational {side.emotionalVsRational.rational}%</span>
|
| 34 |
+
</div>
|
| 35 |
+
)}
|
| 36 |
+
|
| 37 |
+
<h4>Strengths</h4>
|
| 38 |
+
<ul className="list-good">{side.strengths?.map((s, i) => <li key={i}>{s}</li>)}</ul>
|
| 39 |
+
|
| 40 |
+
<h4>Weaknesses</h4>
|
| 41 |
+
<ul className="list-bad">{side.weaknesses?.map((w, i) => <li key={i}>{w}</li>)}</ul>
|
| 42 |
+
|
| 43 |
+
{side.fallacies?.length > 0 && (
|
| 44 |
+
<>
|
| 45 |
+
<h4>Logical Fallacies Detected</h4>
|
| 46 |
+
{side.fallacies.map((f, i) => (
|
| 47 |
+
<div className="fallacy-card" key={i}>
|
| 48 |
+
<span className="fallacy-name">{f.name}</span>
|
| 49 |
+
{f.quote && <p className="fallacy-quote">"{f.quote}"</p>}
|
| 50 |
+
<p className="fallacy-explain">{f.explanation}</p>
|
| 51 |
+
</div>
|
| 52 |
+
))}
|
| 53 |
+
</>
|
| 54 |
+
)}
|
| 55 |
+
|
| 56 |
+
<h4>Evidence Quality</h4>
|
| 57 |
+
<p className="info-text">{side.evidenceQuality}</p>
|
| 58 |
+
|
| 59 |
+
<h4 style={{ color: '#ff6b6b' }}>Devil's Advocate</h4>
|
| 60 |
+
<p className="info-text">{side.devilsAdvocate}</p>
|
| 61 |
+
|
| 62 |
+
<h4 style={{ color: '#4ade80' }}>Steel Man (Strongest Version)</h4>
|
| 63 |
+
<p className="info-text">{side.steelMan}</p>
|
| 64 |
+
|
| 65 |
+
<h4>What Would Change Their Mind?</h4>
|
| 66 |
+
<p className="info-text">{side.whatWouldChangeTheirMind}</p>
|
| 67 |
+
</div>
|
| 68 |
+
);
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
export default function App() {
|
| 72 |
const [topic, setTopic] = useState('');
|
| 73 |
const [side1Name, setSide1Name] = useState('');
|
|
|
|
| 111 |
<div className="loading">
|
| 112 |
<div className="spinner"></div>
|
| 113 |
<p>The Settler is analyzing both sides...</p>
|
| 114 |
+
<p style={{ color: '#888', marginTop: '0.5rem', fontSize: '0.9rem' }}>Checking for logical fallacies, biases, and evidence quality</p>
|
| 115 |
</div>
|
| 116 |
</div>
|
| 117 |
);
|
|
|
|
| 122 |
<div className="app">
|
| 123 |
<h1><span>Argument Settler</span></h1>
|
| 124 |
<div className="results">
|
| 125 |
+
{/* Verdict */}
|
| 126 |
<div className="verdict-card">
|
| 127 |
<div style={{ fontSize: '0.9rem', color: '#888', marginBottom: '0.5rem' }}>THE WINNER IS</div>
|
| 128 |
<div className="winner-name">{result.winner === 'Draw' ? "It's a Draw!" : result.winner}</div>
|
| 129 |
<div className="verdict-text">{result.verdict}</div>
|
| 130 |
</div>
|
| 131 |
|
| 132 |
+
{/* Score comparison */}
|
| 133 |
<div className="scores">
|
| 134 |
<div className="score-card left">
|
| 135 |
<h3>{result.side1?.name}</h3>
|
| 136 |
+
<div className="score-number">{result.side1?.score}<span>/100</span></div>
|
| 137 |
</div>
|
| 138 |
+
<div className="vs-badge">VS</div>
|
| 139 |
<div className="score-card right">
|
| 140 |
<h3>{result.side2?.name}</h3>
|
| 141 |
+
<div className="score-number">{result.side2?.score}<span>/100</span></div>
|
| 142 |
</div>
|
| 143 |
</div>
|
| 144 |
|
| 145 |
+
{/* Detailed panels */}
|
| 146 |
<div className="scores">
|
| 147 |
+
{result.side1 && <SidePanel side={result.side1} color="#ff6b6b" accentClass="accent-left" />}
|
| 148 |
+
{result.side2 && <SidePanel side={result.side2} color="#4ecdc4" accentClass="accent-right" />}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 149 |
</div>
|
| 150 |
|
| 151 |
+
{/* Bias Check */}
|
| 152 |
+
{result.biasCheck && (
|
| 153 |
+
<div className="insight-card bias">
|
| 154 |
+
<h4>Shared Bias Check</h4>
|
| 155 |
+
<p>{result.biasCheck}</p>
|
| 156 |
+
</div>
|
| 157 |
+
)}
|
| 158 |
+
|
| 159 |
+
{/* Common Ground */}
|
| 160 |
{result.commonGround && (
|
| 161 |
+
<div className="insight-card common">
|
| 162 |
<h4>Common Ground</h4>
|
| 163 |
+
<p>{result.commonGround}</p>
|
| 164 |
+
</div>
|
| 165 |
+
)}
|
| 166 |
+
|
| 167 |
+
{/* Compromise */}
|
| 168 |
+
{result.compromise && (
|
| 169 |
+
<div className="insight-card compromise">
|
| 170 |
+
<h4>Suggested Compromise</h4>
|
| 171 |
+
<p>{result.compromise}</p>
|
| 172 |
</div>
|
| 173 |
)}
|
| 174 |
|
| 175 |
+
{/* Plot Twist */}
|
| 176 |
{result.plotTwist && (
|
| 177 |
<div className="plot-twist">
|
| 178 |
<h4>Plot Twist</h4>
|
| 179 |
+
<p>{result.plotTwist}</p>
|
| 180 |
+
</div>
|
| 181 |
+
)}
|
| 182 |
+
|
| 183 |
+
{/* Debate Tips */}
|
| 184 |
+
{result.debateTips && (
|
| 185 |
+
<div className="scores">
|
| 186 |
+
<div className="insight-card tip-left">
|
| 187 |
+
<h4>Tip for {result.side1?.name}</h4>
|
| 188 |
+
<p>{result.debateTips.side1Tip}</p>
|
| 189 |
+
</div>
|
| 190 |
+
<div className="insight-card tip-right">
|
| 191 |
+
<h4>Tip for {result.side2?.name}</h4>
|
| 192 |
+
<p>{result.debateTips.side2Tip}</p>
|
| 193 |
+
</div>
|
| 194 |
</div>
|
| 195 |
)}
|
| 196 |
|
|
|
|
| 203 |
return (
|
| 204 |
<div className="app">
|
| 205 |
<h1><span>Argument Settler</span></h1>
|
| 206 |
+
<p className="subtitle">AI debate judge with logical fallacy detection, evidence scoring, and bias analysis.</p>
|
| 207 |
|
| 208 |
{error && <p style={{ color: '#ff6b6b', textAlign: 'center', marginBottom: '1rem' }}>{error}</p>}
|
| 209 |
|
frontend/src/styles/global.css
CHANGED
|
@@ -1,11 +1,10 @@
|
|
| 1 |
* { margin: 0; padding: 0; box-sizing: border-box; }
|
| 2 |
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: #0a0a0f; color: #e0e0e0; min-height: 100vh; }
|
| 3 |
-
.app { max-width:
|
| 4 |
h1 { text-align: center; font-size: 2.5rem; margin-bottom: 0.5rem; }
|
| 5 |
h1 span { background: linear-gradient(135deg, #ff6b6b, #ffd93d); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
|
| 6 |
.subtitle { text-align: center; color: #888; margin-bottom: 2rem; }
|
| 7 |
.form-section { background: #1a1a2e; border-radius: 12px; padding: 1.5rem; margin-bottom: 1.5rem; }
|
| 8 |
-
.form-section h3 { margin-bottom: 1rem; color: #ffd93d; }
|
| 9 |
.input-group { margin-bottom: 1rem; }
|
| 10 |
.input-group label { display: block; margin-bottom: 0.3rem; color: #aaa; font-size: 0.9rem; }
|
| 11 |
.input-group input, .input-group textarea { width: 100%; padding: 0.8rem; border: 1px solid #333; border-radius: 8px; background: #12121a; color: #e0e0e0; font-size: 1rem; }
|
|
@@ -16,25 +15,76 @@ h1 span { background: linear-gradient(135deg, #ff6b6b, #ffd93d); -webkit-backgro
|
|
| 16 |
.side.right { border-top: 3px solid #4ecdc4; }
|
| 17 |
.btn { width: 100%; padding: 1rem; border: none; border-radius: 8px; font-size: 1.1rem; font-weight: 600; cursor: pointer; background: linear-gradient(135deg, #ff6b6b, #ffd93d); color: #000; transition: transform 0.2s; }
|
| 18 |
.btn:hover { transform: scale(1.02); }
|
| 19 |
-
.btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }
|
| 20 |
.loading { text-align: center; padding: 3rem; }
|
| 21 |
.loading .spinner { width: 50px; height: 50px; border: 4px solid #333; border-top-color: #ffd93d; border-radius: 50%; animation: spin 1s linear infinite; margin: 0 auto 1rem; }
|
| 22 |
@keyframes spin { to { transform: rotate(360deg); } }
|
| 23 |
-
.results { margin-top:
|
|
|
|
| 24 |
.verdict-card { background: linear-gradient(135deg, #1a1a2e, #16213e); border-radius: 12px; padding: 2rem; text-align: center; margin-bottom: 2rem; border: 1px solid #333; }
|
| 25 |
.winner-name { font-size: 2rem; font-weight: 700; color: #ffd93d; margin-bottom: 0.5rem; }
|
| 26 |
-
.verdict-text { color: #ccc; font-size: 1.
|
| 27 |
-
.scores { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; margin-bottom:
|
| 28 |
-
.
|
|
|
|
| 29 |
.score-card h3 { margin-bottom: 0.5rem; }
|
| 30 |
.score-number { font-size: 2.5rem; font-weight: 700; }
|
|
|
|
| 31 |
.score-card.left .score-number { color: #ff6b6b; }
|
| 32 |
.score-card.right .score-number { color: #4ecdc4; }
|
| 33 |
-
|
| 34 |
-
|
|
|
|
|
|
|
|
|
|
| 35 |
.detail-section ul { padding-left: 1.2rem; }
|
| 36 |
-
.detail-section li { margin-bottom: 0.4rem; color: #bbb; }
|
| 37 |
-
.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
.plot-twist h4 { color: #a29bfe; margin-bottom: 0.5rem; }
|
|
|
|
|
|
|
| 39 |
.back-btn { background: #333; color: #fff; margin-top: 1.5rem; }
|
| 40 |
-
@media (max-width: 768px) { .sides-container, .scores { grid-template-columns: 1fr; } h1 { font-size: 1.8rem; } }
|
|
|
|
| 1 |
* { margin: 0; padding: 0; box-sizing: border-box; }
|
| 2 |
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: #0a0a0f; color: #e0e0e0; min-height: 100vh; }
|
| 3 |
+
.app { max-width: 950px; margin: 0 auto; padding: 2rem 1rem; }
|
| 4 |
h1 { text-align: center; font-size: 2.5rem; margin-bottom: 0.5rem; }
|
| 5 |
h1 span { background: linear-gradient(135deg, #ff6b6b, #ffd93d); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
|
| 6 |
.subtitle { text-align: center; color: #888; margin-bottom: 2rem; }
|
| 7 |
.form-section { background: #1a1a2e; border-radius: 12px; padding: 1.5rem; margin-bottom: 1.5rem; }
|
|
|
|
| 8 |
.input-group { margin-bottom: 1rem; }
|
| 9 |
.input-group label { display: block; margin-bottom: 0.3rem; color: #aaa; font-size: 0.9rem; }
|
| 10 |
.input-group input, .input-group textarea { width: 100%; padding: 0.8rem; border: 1px solid #333; border-radius: 8px; background: #12121a; color: #e0e0e0; font-size: 1rem; }
|
|
|
|
| 15 |
.side.right { border-top: 3px solid #4ecdc4; }
|
| 16 |
.btn { width: 100%; padding: 1rem; border: none; border-radius: 8px; font-size: 1.1rem; font-weight: 600; cursor: pointer; background: linear-gradient(135deg, #ff6b6b, #ffd93d); color: #000; transition: transform 0.2s; }
|
| 17 |
.btn:hover { transform: scale(1.02); }
|
|
|
|
| 18 |
.loading { text-align: center; padding: 3rem; }
|
| 19 |
.loading .spinner { width: 50px; height: 50px; border: 4px solid #333; border-top-color: #ffd93d; border-radius: 50%; animation: spin 1s linear infinite; margin: 0 auto 1rem; }
|
| 20 |
@keyframes spin { to { transform: rotate(360deg); } }
|
| 21 |
+
.results { margin-top: 1rem; animation: fadeIn 0.4s ease; }
|
| 22 |
+
@keyframes fadeIn { from { opacity: 0; transform: translateY(15px); } to { opacity: 1; transform: translateY(0); } }
|
| 23 |
.verdict-card { background: linear-gradient(135deg, #1a1a2e, #16213e); border-radius: 12px; padding: 2rem; text-align: center; margin-bottom: 2rem; border: 1px solid #333; }
|
| 24 |
.winner-name { font-size: 2rem; font-weight: 700; color: #ffd93d; margin-bottom: 0.5rem; }
|
| 25 |
+
.verdict-text { color: #ccc; font-size: 1.05rem; line-height: 1.6; }
|
| 26 |
+
.scores { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; margin-bottom: 1.5rem; position: relative; }
|
| 27 |
+
.vs-badge { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); background: #ffd93d; color: #000; font-weight: 800; font-size: 0.8rem; padding: 0.4rem 0.7rem; border-radius: 20px; z-index: 1; }
|
| 28 |
+
.score-card { background: #1a1a2e; border-radius: 12px; padding: 1.5rem; text-align: center; }
|
| 29 |
.score-card h3 { margin-bottom: 0.5rem; }
|
| 30 |
.score-number { font-size: 2.5rem; font-weight: 700; }
|
| 31 |
+
.score-number span { font-size: 1rem; color: #888; }
|
| 32 |
.score-card.left .score-number { color: #ff6b6b; }
|
| 33 |
.score-card.right .score-number { color: #4ecdc4; }
|
| 34 |
+
|
| 35 |
+
/* Detail panels */
|
| 36 |
+
.detail-section { background: #1a1a2e; border-radius: 12px; padding: 1.5rem; }
|
| 37 |
+
.detail-section h3 { margin-bottom: 1rem; font-size: 1.2rem; }
|
| 38 |
+
.detail-section h4 { color: #ffd93d; margin: 1rem 0 0.5rem; font-size: 0.95rem; }
|
| 39 |
.detail-section ul { padding-left: 1.2rem; }
|
| 40 |
+
.detail-section li { margin-bottom: 0.4rem; color: #bbb; line-height: 1.4; }
|
| 41 |
+
.accent-left { border-left: 3px solid #ff6b6b; }
|
| 42 |
+
.accent-right { border-left: 3px solid #4ecdc4; }
|
| 43 |
+
|
| 44 |
+
/* Score bars */
|
| 45 |
+
.mini-scores { margin-bottom: 0.5rem; }
|
| 46 |
+
.score-bar-wrap { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.5rem; }
|
| 47 |
+
.score-bar-label { width: 110px; font-size: 0.8rem; color: #aaa; }
|
| 48 |
+
.score-bar-track { flex: 1; height: 8px; background: #12121a; border-radius: 4px; overflow: hidden; }
|
| 49 |
+
.score-bar-fill { height: 100%; border-radius: 4px; transition: width 0.6s ease; }
|
| 50 |
+
.score-bar-val { width: 30px; text-align: right; font-size: 0.85rem; font-weight: 600; }
|
| 51 |
+
|
| 52 |
+
/* Emotional vs Rational bar */
|
| 53 |
+
.emo-rational { display: flex; align-items: center; gap: 0.5rem; margin: 0.8rem 0; font-size: 0.8rem; color: #aaa; }
|
| 54 |
+
.emo-bar { flex: 1; height: 8px; background: #4ecdc4; border-radius: 4px; overflow: hidden; }
|
| 55 |
+
.emo-fill { height: 100%; background: #ff6b6b; border-radius: 4px; }
|
| 56 |
+
|
| 57 |
+
/* Lists */
|
| 58 |
+
.list-good li::marker { color: #4ade80; }
|
| 59 |
+
.list-bad li::marker { color: #ef4444; }
|
| 60 |
+
|
| 61 |
+
/* Fallacy cards */
|
| 62 |
+
.fallacy-card { background: rgba(239, 68, 68, 0.08); border: 1px solid rgba(239, 68, 68, 0.2); border-radius: 8px; padding: 0.8rem; margin-bottom: 0.5rem; }
|
| 63 |
+
.fallacy-name { display: inline-block; background: rgba(239, 68, 68, 0.2); color: #ef4444; padding: 0.15rem 0.5rem; border-radius: 4px; font-size: 0.75rem; font-weight: 700; letter-spacing: 0.3px; }
|
| 64 |
+
.fallacy-quote { color: #ff9999; font-style: italic; margin: 0.4rem 0; font-size: 0.85rem; }
|
| 65 |
+
.fallacy-explain { color: #aaa; font-size: 0.85rem; line-height: 1.4; }
|
| 66 |
+
|
| 67 |
+
.info-text { color: #bbb; line-height: 1.5; font-size: 0.95rem; }
|
| 68 |
+
|
| 69 |
+
/* Insight cards */
|
| 70 |
+
.insight-card { background: #1a1a2e; border-radius: 12px; padding: 1.5rem; margin-bottom: 1rem; }
|
| 71 |
+
.insight-card h4 { margin-bottom: 0.5rem; }
|
| 72 |
+
.insight-card p { color: #bbb; line-height: 1.5; }
|
| 73 |
+
.insight-card.bias { border-left: 3px solid #fbbf24; }
|
| 74 |
+
.insight-card.bias h4 { color: #fbbf24; }
|
| 75 |
+
.insight-card.common { border-left: 3px solid #4ade80; }
|
| 76 |
+
.insight-card.common h4 { color: #4ade80; }
|
| 77 |
+
.insight-card.compromise { border-left: 3px solid #60a5fa; }
|
| 78 |
+
.insight-card.compromise h4 { color: #60a5fa; }
|
| 79 |
+
.insight-card.tip-left { border-left: 3px solid #ff6b6b; }
|
| 80 |
+
.insight-card.tip-left h4 { color: #ff6b6b; }
|
| 81 |
+
.insight-card.tip-right { border-left: 3px solid #4ecdc4; }
|
| 82 |
+
.insight-card.tip-right h4 { color: #4ecdc4; }
|
| 83 |
+
|
| 84 |
+
/* Plot twist */
|
| 85 |
+
.plot-twist { background: linear-gradient(135deg, #2d1b69, #1a1a2e); border-radius: 12px; padding: 1.5rem; margin-bottom: 1.5rem; border: 1px solid #6c5ce7; }
|
| 86 |
.plot-twist h4 { color: #a29bfe; margin-bottom: 0.5rem; }
|
| 87 |
+
.plot-twist p { color: #bbb; line-height: 1.5; }
|
| 88 |
+
|
| 89 |
.back-btn { background: #333; color: #fff; margin-top: 1.5rem; }
|
| 90 |
+
@media (max-width: 768px) { .sides-container, .scores { grid-template-columns: 1fr; } h1 { font-size: 1.8rem; } .vs-badge { display: none; } }
|