Claude commited on
Add EN/FR language toggle, English by default
Browse filesInterface is now in English by default with a FR button in the
top-right corner to switch to French. All UI labels, placeholders,
error messages, and analysis scores are translated. The toggle
switches instantly without page reload.
https://claude.ai/code/session_015z3yZxNNfXF63JuQDuPbEG
- static/app.js +116 -17
- static/index.html +29 -26
- static/style.css +19 -0
static/app.js
CHANGED
|
@@ -6,6 +6,105 @@
|
|
| 6 |
(function () {
|
| 7 |
"use strict";
|
| 8 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
/* ===== State (in-memory only, lost on tab close) ===== */
|
| 10 |
var state = {
|
| 11 |
mode: "TUTOR",
|
|
@@ -275,7 +374,7 @@
|
|
| 275 |
.then(function (data) {
|
| 276 |
setTyping(false);
|
| 277 |
if (!data.reply) {
|
| 278 |
-
addMessage("assistant", "
|
| 279 |
btnSend.disabled = false;
|
| 280 |
return;
|
| 281 |
}
|
|
@@ -291,7 +390,7 @@
|
|
| 291 |
.catch(function (err) {
|
| 292 |
setTyping(false);
|
| 293 |
console.error("sendMessage error:", err);
|
| 294 |
-
addMessage("assistant",
|
| 295 |
btnSend.disabled = false;
|
| 296 |
});
|
| 297 |
}
|
|
@@ -318,20 +417,17 @@
|
|
| 318 |
.catch(function () {
|
| 319 |
setTyping(false);
|
| 320 |
btnEnd.disabled = false;
|
| 321 |
-
alert("
|
| 322 |
});
|
| 323 |
}
|
| 324 |
|
| 325 |
/* ===== Analysis rendering ===== */
|
| 326 |
function renderAnalysis(data) {
|
| 327 |
-
var
|
| 328 |
-
|
| 329 |
-
|
| 330 |
-
{ key:
|
| 331 |
-
|
| 332 |
-
{ key: "reflectionScore", label: "Reflexion" },
|
| 333 |
-
{ key: "integrityScore", label: "Integrite" }
|
| 334 |
-
];
|
| 335 |
|
| 336 |
scoresGrid.innerHTML = "";
|
| 337 |
scores.forEach(function (s) {
|
|
@@ -345,7 +441,7 @@
|
|
| 345 |
scoresGrid.appendChild(card);
|
| 346 |
});
|
| 347 |
|
| 348 |
-
summaryEl.textContent = data.summary || "
|
| 349 |
|
| 350 |
strengthsEl.innerHTML = "";
|
| 351 |
(data.keyStrengths || []).forEach(function (s) {
|
|
@@ -452,7 +548,7 @@
|
|
| 452 |
state.phaseTurns = 0;
|
| 453 |
state.history = [];
|
| 454 |
state.timestamps = [];
|
| 455 |
-
modeBadge.textContent = state.mode === "TUTOR" ? "
|
| 456 |
topicBadge.textContent = topic;
|
| 457 |
|
| 458 |
// Show doc count badge
|
|
@@ -479,7 +575,7 @@
|
|
| 479 |
method: "POST",
|
| 480 |
headers: { "Content-Type": "application/json" },
|
| 481 |
body: JSON.stringify({
|
| 482 |
-
message: "
|
| 483 |
mode: state.mode,
|
| 484 |
topic: state.topic,
|
| 485 |
phase: state.phase,
|
|
@@ -498,7 +594,7 @@
|
|
| 498 |
.then(function (data) {
|
| 499 |
setTyping(false);
|
| 500 |
if (!data.reply) {
|
| 501 |
-
addMessage("assistant", "
|
| 502 |
btnSend.disabled = false;
|
| 503 |
return;
|
| 504 |
}
|
|
@@ -514,7 +610,7 @@
|
|
| 514 |
.catch(function (err) {
|
| 515 |
setTyping(false);
|
| 516 |
console.error("startSession error:", err);
|
| 517 |
-
addMessage("assistant",
|
| 518 |
btnSend.disabled = false;
|
| 519 |
});
|
| 520 |
}
|
|
@@ -535,7 +631,7 @@
|
|
| 535 |
// End session -> analysis
|
| 536 |
btnEnd.addEventListener("click", function () {
|
| 537 |
if (state.history.length === 0) {
|
| 538 |
-
alert("
|
| 539 |
return;
|
| 540 |
}
|
| 541 |
requestAnalysis();
|
|
@@ -553,4 +649,7 @@
|
|
| 553 |
// Load existing docs on startup
|
| 554 |
loadDocumentList();
|
| 555 |
|
|
|
|
|
|
|
|
|
|
| 556 |
})();
|
|
|
|
| 6 |
(function () {
|
| 7 |
"use strict";
|
| 8 |
|
| 9 |
+
/* ===== i18n ===== */
|
| 10 |
+
var LANG = {
|
| 11 |
+
en: {
|
| 12 |
+
subtitle: "Socratic Learning Companion",
|
| 13 |
+
topicLabel: "Exploration topic",
|
| 14 |
+
topicPlaceholder: "E.g.: Artificial intelligence in professional training",
|
| 15 |
+
docsLabel: "Reference documents (optional)",
|
| 16 |
+
uploadText: "Drag your files here or click to select",
|
| 17 |
+
uploadHint: "PDF, PPTX, TXT or ZIP \u2014 multiple files allowed",
|
| 18 |
+
modeLabel: "Mode",
|
| 19 |
+
modeTutor: "Tutor",
|
| 20 |
+
modeTutorDesc: "Supportive guidance, open-ended questions",
|
| 21 |
+
modeCritic: "Critic",
|
| 22 |
+
modeCriticDesc: "Devil's advocate, tests logical weaknesses",
|
| 23 |
+
btnStart: "Start session",
|
| 24 |
+
btnEnd: "End session",
|
| 25 |
+
btnRestart: "Restart",
|
| 26 |
+
chatPlaceholder: "Type your thoughts here...",
|
| 27 |
+
btnSend: "Send",
|
| 28 |
+
reportTitle: "Session Report",
|
| 29 |
+
summaryTitle: "Summary",
|
| 30 |
+
strengthsTitle: "Key strengths",
|
| 31 |
+
weaknessesTitle: "Areas for improvement",
|
| 32 |
+
rhythmTitle: "Pace",
|
| 33 |
+
rhythmText: "Responses under 8 seconds:",
|
| 34 |
+
btnExport: "Export JSON",
|
| 35 |
+
btnNewSession: "New session",
|
| 36 |
+
modeBadgeTutor: "Tutor",
|
| 37 |
+
modeBadgeCritic: "Critic",
|
| 38 |
+
errorEmpty: "No response from server. Check API configuration.",
|
| 39 |
+
errorConnection: "Connection error. Please try again.",
|
| 40 |
+
errorNoExchange: "No exchanges to analyze.",
|
| 41 |
+
errorAnalysis: "Analysis error. Please try again.",
|
| 42 |
+
startMessage: "Hello, I would like to explore the topic: ",
|
| 43 |
+
scoreLabels: ["Reasoning", "Clarity", "Skepticism", "Process", "Reflection", "Integrity"],
|
| 44 |
+
noSummary: "No summary available.",
|
| 45 |
+
langBtn: "FR"
|
| 46 |
+
},
|
| 47 |
+
fr: {
|
| 48 |
+
subtitle: "Compagnon socratique d'apprentissage",
|
| 49 |
+
topicLabel: "Sujet d'exploration",
|
| 50 |
+
topicPlaceholder: "Ex : L'intelligence artificielle en formation professionnelle",
|
| 51 |
+
docsLabel: "Documents de reference (optionnel)",
|
| 52 |
+
uploadText: "Glisse tes fichiers ici ou clique pour selectionner",
|
| 53 |
+
uploadHint: "PDF, PPTX, TXT ou ZIP \u2014 plusieurs fichiers possibles",
|
| 54 |
+
modeLabel: "Mode",
|
| 55 |
+
modeTutor: "Tuteur",
|
| 56 |
+
modeTutorDesc: "Accompagnement bienveillant, questions ouvertes",
|
| 57 |
+
modeCritic: "Critique",
|
| 58 |
+
modeCriticDesc: "Avocat du diable, teste les failles logiques",
|
| 59 |
+
btnStart: "Commencer la session",
|
| 60 |
+
btnEnd: "Terminer la session",
|
| 61 |
+
btnRestart: "Recommencer",
|
| 62 |
+
chatPlaceholder: "Tape ta reflexion ici...",
|
| 63 |
+
btnSend: "Envoyer",
|
| 64 |
+
reportTitle: "Rapport de session",
|
| 65 |
+
summaryTitle: "Bilan",
|
| 66 |
+
strengthsTitle: "Points forts",
|
| 67 |
+
weaknessesTitle: "Axes d'amelioration",
|
| 68 |
+
rhythmTitle: "Rythme",
|
| 69 |
+
rhythmText: "Reponses en moins de 8 secondes :",
|
| 70 |
+
btnExport: "Exporter JSON",
|
| 71 |
+
btnNewSession: "Nouvelle session",
|
| 72 |
+
modeBadgeTutor: "Tuteur",
|
| 73 |
+
modeBadgeCritic: "Critique",
|
| 74 |
+
errorEmpty: "Reponse vide du serveur. Verifiez la configuration API.",
|
| 75 |
+
errorConnection: "Erreur de connexion. Veuillez reessayer.",
|
| 76 |
+
errorNoExchange: "Aucun echange a analyser.",
|
| 77 |
+
errorAnalysis: "Erreur lors de l'analyse. Veuillez reessayer.",
|
| 78 |
+
startMessage: "Bonjour, je souhaite explorer le sujet : ",
|
| 79 |
+
scoreLabels: ["Raisonnement", "Clarte", "Scepticisme", "Processus", "Reflexion", "Integrite"],
|
| 80 |
+
noSummary: "Aucun bilan disponible.",
|
| 81 |
+
langBtn: "EN"
|
| 82 |
+
}
|
| 83 |
+
};
|
| 84 |
+
|
| 85 |
+
var currentLang = "en";
|
| 86 |
+
|
| 87 |
+
function t(key) {
|
| 88 |
+
return LANG[currentLang][key] || key;
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
function applyLanguage() {
|
| 92 |
+
document.querySelectorAll("[data-i18n]").forEach(function (el) {
|
| 93 |
+
el.textContent = t(el.dataset.i18n);
|
| 94 |
+
});
|
| 95 |
+
document.querySelectorAll("[data-i18n-placeholder]").forEach(function (el) {
|
| 96 |
+
el.placeholder = t(el.dataset.i18nPlaceholder);
|
| 97 |
+
});
|
| 98 |
+
document.getElementById("btn-lang").textContent = t("langBtn");
|
| 99 |
+
document.documentElement.lang = currentLang === "fr" ? "fr" : "en";
|
| 100 |
+
document.title = currentLang === "en" ? "AIM - Learning Companion" : "AIM - Compagnon d'apprentissage";
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
document.getElementById("btn-lang").addEventListener("click", function () {
|
| 104 |
+
currentLang = currentLang === "en" ? "fr" : "en";
|
| 105 |
+
applyLanguage();
|
| 106 |
+
});
|
| 107 |
+
|
| 108 |
/* ===== State (in-memory only, lost on tab close) ===== */
|
| 109 |
var state = {
|
| 110 |
mode: "TUTOR",
|
|
|
|
| 374 |
.then(function (data) {
|
| 375 |
setTyping(false);
|
| 376 |
if (!data.reply) {
|
| 377 |
+
addMessage("assistant", t("errorEmpty"));
|
| 378 |
btnSend.disabled = false;
|
| 379 |
return;
|
| 380 |
}
|
|
|
|
| 390 |
.catch(function (err) {
|
| 391 |
setTyping(false);
|
| 392 |
console.error("sendMessage error:", err);
|
| 393 |
+
addMessage("assistant", err.message || t("errorConnection"));
|
| 394 |
btnSend.disabled = false;
|
| 395 |
});
|
| 396 |
}
|
|
|
|
| 417 |
.catch(function () {
|
| 418 |
setTyping(false);
|
| 419 |
btnEnd.disabled = false;
|
| 420 |
+
alert(t("errorAnalysis"));
|
| 421 |
});
|
| 422 |
}
|
| 423 |
|
| 424 |
/* ===== Analysis rendering ===== */
|
| 425 |
function renderAnalysis(data) {
|
| 426 |
+
var scoreKeys = ["reasoningScore", "clarityScore", "skepticismScore", "processScore", "reflectionScore", "integrityScore"];
|
| 427 |
+
var labels = t("scoreLabels");
|
| 428 |
+
var scores = scoreKeys.map(function (key, i) {
|
| 429 |
+
return { key: key, label: labels[i] };
|
| 430 |
+
});
|
|
|
|
|
|
|
|
|
|
| 431 |
|
| 432 |
scoresGrid.innerHTML = "";
|
| 433 |
scores.forEach(function (s) {
|
|
|
|
| 441 |
scoresGrid.appendChild(card);
|
| 442 |
});
|
| 443 |
|
| 444 |
+
summaryEl.textContent = data.summary || t("noSummary");
|
| 445 |
|
| 446 |
strengthsEl.innerHTML = "";
|
| 447 |
(data.keyStrengths || []).forEach(function (s) {
|
|
|
|
| 548 |
state.phaseTurns = 0;
|
| 549 |
state.history = [];
|
| 550 |
state.timestamps = [];
|
| 551 |
+
modeBadge.textContent = state.mode === "TUTOR" ? t("modeBadgeTutor") : t("modeBadgeCritic");
|
| 552 |
topicBadge.textContent = topic;
|
| 553 |
|
| 554 |
// Show doc count badge
|
|
|
|
| 575 |
method: "POST",
|
| 576 |
headers: { "Content-Type": "application/json" },
|
| 577 |
body: JSON.stringify({
|
| 578 |
+
message: t("startMessage") + state.topic,
|
| 579 |
mode: state.mode,
|
| 580 |
topic: state.topic,
|
| 581 |
phase: state.phase,
|
|
|
|
| 594 |
.then(function (data) {
|
| 595 |
setTyping(false);
|
| 596 |
if (!data.reply) {
|
| 597 |
+
addMessage("assistant", t("errorEmpty"));
|
| 598 |
btnSend.disabled = false;
|
| 599 |
return;
|
| 600 |
}
|
|
|
|
| 610 |
.catch(function (err) {
|
| 611 |
setTyping(false);
|
| 612 |
console.error("startSession error:", err);
|
| 613 |
+
addMessage("assistant", err.message || t("errorConnection"));
|
| 614 |
btnSend.disabled = false;
|
| 615 |
});
|
| 616 |
}
|
|
|
|
| 631 |
// End session -> analysis
|
| 632 |
btnEnd.addEventListener("click", function () {
|
| 633 |
if (state.history.length === 0) {
|
| 634 |
+
alert(t("errorNoExchange"));
|
| 635 |
return;
|
| 636 |
}
|
| 637 |
requestAnalysis();
|
|
|
|
| 649 |
// Load existing docs on startup
|
| 650 |
loadDocumentList();
|
| 651 |
|
| 652 |
+
// Apply default language
|
| 653 |
+
applyLanguage();
|
| 654 |
+
|
| 655 |
})();
|
static/index.html
CHANGED
|
@@ -1,29 +1,32 @@
|
|
| 1 |
<!DOCTYPE html>
|
| 2 |
-
<html lang="
|
| 3 |
<head>
|
| 4 |
<meta charset="UTF-8">
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
-
<title>AIM -
|
| 7 |
<link rel="stylesheet" href="/static/style.css">
|
| 8 |
</head>
|
| 9 |
<body>
|
| 10 |
|
|
|
|
|
|
|
|
|
|
| 11 |
<!-- ===== Setup Screen ===== -->
|
| 12 |
<div id="setup-screen" class="screen active">
|
| 13 |
<h1>AIM</h1>
|
| 14 |
-
<p class="subtitle">
|
| 15 |
|
| 16 |
<div class="form-group">
|
| 17 |
-
<label for="topic-input">
|
| 18 |
-
<input type="text" id="topic-input" placeholder="
|
| 19 |
</div>
|
| 20 |
|
| 21 |
<div class="form-group">
|
| 22 |
-
<label>
|
| 23 |
<div class="upload-zone" id="upload-zone">
|
| 24 |
<div class="upload-icon">+</div>
|
| 25 |
-
<div class="upload-text">
|
| 26 |
-
<div class="upload-hint">PDF, PPTX, TXT
|
| 27 |
<input type="file" id="file-input" multiple accept=".pdf,.pptx,.ppt,.txt,.zip" hidden>
|
| 28 |
</div>
|
| 29 |
<div class="upload-list" id="upload-list"></div>
|
|
@@ -31,20 +34,20 @@
|
|
| 31 |
</div>
|
| 32 |
|
| 33 |
<div class="form-group">
|
| 34 |
-
<label>Mode</label>
|
| 35 |
<div class="mode-selector">
|
| 36 |
<div class="mode-btn selected" data-mode="TUTOR">
|
| 37 |
-
<div class="mode-title">
|
| 38 |
-
<div class="mode-desc">
|
| 39 |
</div>
|
| 40 |
<div class="mode-btn" data-mode="CRITIC">
|
| 41 |
-
<div class="mode-title">
|
| 42 |
-
<div class="mode-desc">
|
| 43 |
</div>
|
| 44 |
</div>
|
| 45 |
</div>
|
| 46 |
|
| 47 |
-
<button id="btn-start" class="btn-primary" disabled>
|
| 48 |
</div>
|
| 49 |
|
| 50 |
<!-- ===== Chat Screen ===== -->
|
|
@@ -56,8 +59,8 @@
|
|
| 56 |
<span id="docs-badge" class="badge badge-docs" style="display:none"></span>
|
| 57 |
</div>
|
| 58 |
<div class="chat-header-right">
|
| 59 |
-
<button id="btn-end-session" class="btn-end">
|
| 60 |
-
<button id="btn-reset" class="btn-reset">
|
| 61 |
</div>
|
| 62 |
</div>
|
| 63 |
|
|
@@ -71,40 +74,40 @@
|
|
| 71 |
</div>
|
| 72 |
|
| 73 |
<div class="input-bar">
|
| 74 |
-
<input type="text" id="chat-input" placeholder="
|
| 75 |
-
<button id="btn-send" class="btn-send">
|
| 76 |
</div>
|
| 77 |
</div>
|
| 78 |
|
| 79 |
<!-- ===== Analysis Screen ===== -->
|
| 80 |
<div id="analysis-screen" class="screen">
|
| 81 |
-
<h2>
|
| 82 |
|
| 83 |
<div class="scores-grid" id="scores-grid"></div>
|
| 84 |
|
| 85 |
<div class="analysis-section">
|
| 86 |
-
<h3>
|
| 87 |
<p id="analysis-summary"></p>
|
| 88 |
</div>
|
| 89 |
|
| 90 |
<div class="analysis-section">
|
| 91 |
-
<h3>
|
| 92 |
<ul id="analysis-strengths"></ul>
|
| 93 |
</div>
|
| 94 |
|
| 95 |
<div class="analysis-section">
|
| 96 |
-
<h3>
|
| 97 |
<ul id="analysis-weaknesses"></ul>
|
| 98 |
</div>
|
| 99 |
|
| 100 |
<div class="analysis-section">
|
| 101 |
-
<h3>
|
| 102 |
-
<p>
|
| 103 |
</div>
|
| 104 |
|
| 105 |
<div class="analysis-actions">
|
| 106 |
-
<button id="btn-export" class="btn-secondary">
|
| 107 |
-
<button id="btn-new-session" class="btn-primary">
|
| 108 |
</div>
|
| 109 |
</div>
|
| 110 |
|
|
|
|
| 1 |
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
<head>
|
| 4 |
<meta charset="UTF-8">
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>AIM - Learning Companion</title>
|
| 7 |
<link rel="stylesheet" href="/static/style.css">
|
| 8 |
</head>
|
| 9 |
<body>
|
| 10 |
|
| 11 |
+
<!-- Language toggle (visible on all screens) -->
|
| 12 |
+
<button id="btn-lang" class="btn-lang" title="Switch language">FR</button>
|
| 13 |
+
|
| 14 |
<!-- ===== Setup Screen ===== -->
|
| 15 |
<div id="setup-screen" class="screen active">
|
| 16 |
<h1>AIM</h1>
|
| 17 |
+
<p class="subtitle" data-i18n="subtitle">Socratic Learning Companion</p>
|
| 18 |
|
| 19 |
<div class="form-group">
|
| 20 |
+
<label for="topic-input" data-i18n="topicLabel">Exploration topic</label>
|
| 21 |
+
<input type="text" id="topic-input" data-i18n-placeholder="topicPlaceholder" placeholder="E.g.: Artificial intelligence in professional training">
|
| 22 |
</div>
|
| 23 |
|
| 24 |
<div class="form-group">
|
| 25 |
+
<label data-i18n="docsLabel">Reference documents (optional)</label>
|
| 26 |
<div class="upload-zone" id="upload-zone">
|
| 27 |
<div class="upload-icon">+</div>
|
| 28 |
+
<div class="upload-text" data-i18n="uploadText">Drag your files here or click to select</div>
|
| 29 |
+
<div class="upload-hint" data-i18n="uploadHint">PDF, PPTX, TXT or ZIP — multiple files allowed</div>
|
| 30 |
<input type="file" id="file-input" multiple accept=".pdf,.pptx,.ppt,.txt,.zip" hidden>
|
| 31 |
</div>
|
| 32 |
<div class="upload-list" id="upload-list"></div>
|
|
|
|
| 34 |
</div>
|
| 35 |
|
| 36 |
<div class="form-group">
|
| 37 |
+
<label data-i18n="modeLabel">Mode</label>
|
| 38 |
<div class="mode-selector">
|
| 39 |
<div class="mode-btn selected" data-mode="TUTOR">
|
| 40 |
+
<div class="mode-title" data-i18n="modeTutor">Tutor</div>
|
| 41 |
+
<div class="mode-desc" data-i18n="modeTutorDesc">Supportive guidance, open-ended questions</div>
|
| 42 |
</div>
|
| 43 |
<div class="mode-btn" data-mode="CRITIC">
|
| 44 |
+
<div class="mode-title" data-i18n="modeCritic">Critic</div>
|
| 45 |
+
<div class="mode-desc" data-i18n="modeCriticDesc">Devil's advocate, tests logical weaknesses</div>
|
| 46 |
</div>
|
| 47 |
</div>
|
| 48 |
</div>
|
| 49 |
|
| 50 |
+
<button id="btn-start" class="btn-primary" disabled data-i18n="btnStart">Start session</button>
|
| 51 |
</div>
|
| 52 |
|
| 53 |
<!-- ===== Chat Screen ===== -->
|
|
|
|
| 59 |
<span id="docs-badge" class="badge badge-docs" style="display:none"></span>
|
| 60 |
</div>
|
| 61 |
<div class="chat-header-right">
|
| 62 |
+
<button id="btn-end-session" class="btn-end" data-i18n="btnEnd">End session</button>
|
| 63 |
+
<button id="btn-reset" class="btn-reset" data-i18n="btnRestart">Restart</button>
|
| 64 |
</div>
|
| 65 |
</div>
|
| 66 |
|
|
|
|
| 74 |
</div>
|
| 75 |
|
| 76 |
<div class="input-bar">
|
| 77 |
+
<input type="text" id="chat-input" data-i18n-placeholder="chatPlaceholder" placeholder="Type your thoughts here...">
|
| 78 |
+
<button id="btn-send" class="btn-send" data-i18n="btnSend">Send</button>
|
| 79 |
</div>
|
| 80 |
</div>
|
| 81 |
|
| 82 |
<!-- ===== Analysis Screen ===== -->
|
| 83 |
<div id="analysis-screen" class="screen">
|
| 84 |
+
<h2 data-i18n="reportTitle">Session Report</h2>
|
| 85 |
|
| 86 |
<div class="scores-grid" id="scores-grid"></div>
|
| 87 |
|
| 88 |
<div class="analysis-section">
|
| 89 |
+
<h3 data-i18n="summaryTitle">Summary</h3>
|
| 90 |
<p id="analysis-summary"></p>
|
| 91 |
</div>
|
| 92 |
|
| 93 |
<div class="analysis-section">
|
| 94 |
+
<h3 data-i18n="strengthsTitle">Key strengths</h3>
|
| 95 |
<ul id="analysis-strengths"></ul>
|
| 96 |
</div>
|
| 97 |
|
| 98 |
<div class="analysis-section">
|
| 99 |
+
<h3 data-i18n="weaknessesTitle">Areas for improvement</h3>
|
| 100 |
<ul id="analysis-weaknesses"></ul>
|
| 101 |
</div>
|
| 102 |
|
| 103 |
<div class="analysis-section">
|
| 104 |
+
<h3 data-i18n="rhythmTitle">Pace</h3>
|
| 105 |
+
<p><span data-i18n="rhythmText">Responses under 8 seconds:</span> <span id="rhythm-count" class="rhythm-badge">0</span></p>
|
| 106 |
</div>
|
| 107 |
|
| 108 |
<div class="analysis-actions">
|
| 109 |
+
<button id="btn-export" class="btn-secondary" data-i18n="btnExport">Export JSON</button>
|
| 110 |
+
<button id="btn-new-session" class="btn-primary" data-i18n="btnNewSession">New session</button>
|
| 111 |
</div>
|
| 112 |
</div>
|
| 113 |
|
static/style.css
CHANGED
|
@@ -15,6 +15,25 @@
|
|
| 15 |
--radius-sm: 8px;
|
| 16 |
}
|
| 17 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
| 19 |
|
| 20 |
body {
|
|
|
|
| 15 |
--radius-sm: 8px;
|
| 16 |
}
|
| 17 |
|
| 18 |
+
.btn-lang {
|
| 19 |
+
position: fixed;
|
| 20 |
+
top: 12px;
|
| 21 |
+
right: 16px;
|
| 22 |
+
z-index: 100;
|
| 23 |
+
background: var(--bg-tertiary);
|
| 24 |
+
color: var(--text-primary);
|
| 25 |
+
border: 1px solid var(--border);
|
| 26 |
+
border-radius: var(--radius-sm);
|
| 27 |
+
padding: 6px 14px;
|
| 28 |
+
font-size: 0.85rem;
|
| 29 |
+
font-weight: 600;
|
| 30 |
+
cursor: pointer;
|
| 31 |
+
transition: background 0.2s;
|
| 32 |
+
}
|
| 33 |
+
.btn-lang:hover {
|
| 34 |
+
background: var(--accent);
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
| 38 |
|
| 39 |
body {
|