1100 lines
43 KiB
HTML
Executable File
1100 lines
43 KiB
HTML
Executable File
<!DOCTYPE html>
|
||
<html lang="fr">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>PhysMath — Outils Lycée / Prépa</title>
|
||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=Fira+Code:wght@400;500&display=swap" rel="stylesheet">
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjs/12.4.3/math.min.js"></script>
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.min.js"></script>
|
||
<style>
|
||
:root {
|
||
--bg: #282a36;
|
||
--bg2: #1e1f29;
|
||
--bg3: #21222c;
|
||
--surface: #343746;
|
||
--surface2: #44475a;
|
||
--border: #6272a4;
|
||
--fg: #f8f8f2;
|
||
--fg2: #cdd6f4;
|
||
--comment: #6272a4;
|
||
--cyan: #8be9fd;
|
||
--green: #50fa7b;
|
||
--orange: #ffb86c;
|
||
--pink: #ff79c6;
|
||
--purple: #bd93f9;
|
||
--red: #ff5555;
|
||
--yellow: #f1fa8c;
|
||
--font-mono: 'JetBrains Mono', 'Fira Code', monospace;
|
||
--radius: 8px;
|
||
--radius-lg: 12px;
|
||
}
|
||
|
||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||
|
||
body {
|
||
background: var(--bg2);
|
||
color: var(--fg);
|
||
font-family: var(--font-mono);
|
||
font-size: 14px;
|
||
line-height: 1.6;
|
||
min-height: 100vh;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
/* HEADER */
|
||
header {
|
||
background: var(--bg3);
|
||
border-bottom: 1px solid var(--surface2);
|
||
padding: 0 24px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0;
|
||
height: 52px;
|
||
position: sticky;
|
||
top: 0;
|
||
z-index: 100;
|
||
}
|
||
|
||
.logo {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: var(--purple);
|
||
margin-right: 32px;
|
||
letter-spacing: -0.5px;
|
||
}
|
||
.logo span { color: var(--cyan); }
|
||
|
||
.tabs {
|
||
display: flex;
|
||
gap: 2px;
|
||
height: 100%;
|
||
align-items: flex-end;
|
||
}
|
||
|
||
.tab {
|
||
padding: 8px 18px;
|
||
cursor: pointer;
|
||
border: none;
|
||
background: transparent;
|
||
color: var(--comment);
|
||
font-family: var(--font-mono);
|
||
font-size: 13px;
|
||
border-bottom: 2px solid transparent;
|
||
transition: all 0.15s;
|
||
height: 44px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
white-space: nowrap;
|
||
}
|
||
.tab:hover { color: var(--fg2); background: var(--surface); border-radius: var(--radius) var(--radius) 0 0; }
|
||
.tab.active { color: var(--fg); border-bottom-color: var(--purple); }
|
||
.tab .dot { width: 7px; height: 7px; border-radius: 50%; }
|
||
.tab:nth-child(1) .dot { background: var(--cyan); }
|
||
.tab:nth-child(2) .dot { background: var(--green); }
|
||
.tab:nth-child(3) .dot { background: var(--orange); }
|
||
.tab:nth-child(4) .dot { background: var(--pink); }
|
||
|
||
/* PAGES */
|
||
.page { display: none; padding: 24px; flex: 1; }
|
||
.page.active { display: flex; flex-direction: column; gap: 20px; }
|
||
|
||
/* GRID LAYOUT */
|
||
.two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
|
||
.full { grid-column: 1 / -1; }
|
||
|
||
/* PANELS */
|
||
.panel {
|
||
background: var(--bg3);
|
||
border: 1px solid var(--surface2);
|
||
border-radius: var(--radius-lg);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.panel-header {
|
||
padding: 12px 16px;
|
||
background: var(--bg3);
|
||
border-bottom: 1px solid var(--surface);
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
color: var(--comment);
|
||
letter-spacing: 0.5px;
|
||
text-transform: uppercase;
|
||
}
|
||
.panel-header .accent { width: 6px; height: 6px; border-radius: 50%; flex-shrink: 0; }
|
||
|
||
.panel-body { padding: 16px; }
|
||
|
||
/* INPUTS */
|
||
.input-row {
|
||
display: flex;
|
||
gap: 8px;
|
||
align-items: center;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.input-row label {
|
||
font-size: 12px;
|
||
color: var(--comment);
|
||
min-width: 60px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
input[type="text"], input[type="number"], select, textarea {
|
||
background: var(--bg);
|
||
border: 1px solid var(--surface2);
|
||
border-radius: var(--radius);
|
||
color: var(--fg);
|
||
font-family: var(--font-mono);
|
||
font-size: 14px;
|
||
padding: 7px 12px;
|
||
outline: none;
|
||
transition: border-color 0.15s;
|
||
width: 100%;
|
||
}
|
||
input:focus, select:focus, textarea:focus {
|
||
border-color: var(--purple);
|
||
box-shadow: 0 0 0 2px rgba(189,147,249,0.15);
|
||
}
|
||
textarea { resize: vertical; min-height: 80px; }
|
||
select { cursor: pointer; }
|
||
select option { background: var(--bg); }
|
||
|
||
/* BUTTONS */
|
||
.btn {
|
||
padding: 7px 16px;
|
||
border-radius: var(--radius);
|
||
border: 1px solid;
|
||
font-family: var(--font-mono);
|
||
font-size: 13px;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
transition: all 0.15s;
|
||
white-space: nowrap;
|
||
}
|
||
.btn:active { transform: scale(0.97); }
|
||
|
||
.btn-purple { background: rgba(189,147,249,0.15); border-color: var(--purple); color: var(--purple); }
|
||
.btn-purple:hover { background: rgba(189,147,249,0.25); }
|
||
.btn-cyan { background: rgba(139,233,253,0.1); border-color: var(--cyan); color: var(--cyan); }
|
||
.btn-cyan:hover { background: rgba(139,233,253,0.2); }
|
||
.btn-green { background: rgba(80,250,123,0.1); border-color: var(--green); color: var(--green); }
|
||
.btn-green:hover { background: rgba(80,250,123,0.2); }
|
||
.btn-red { background: rgba(255,85,85,0.1); border-color: var(--red); color: var(--red); }
|
||
.btn-red:hover { background: rgba(255,85,85,0.2); }
|
||
|
||
/* OUTPUT ZONES */
|
||
.output {
|
||
background: var(--bg);
|
||
border: 1px solid var(--surface);
|
||
border-radius: var(--radius);
|
||
padding: 12px;
|
||
font-size: 13px;
|
||
min-height: 36px;
|
||
color: var(--green);
|
||
font-family: var(--font-mono);
|
||
white-space: pre-wrap;
|
||
word-break: break-all;
|
||
}
|
||
.output.error { color: var(--red); }
|
||
.output.info { color: var(--cyan); }
|
||
|
||
/* CANVAS */
|
||
#plot-canvas-wrap {
|
||
background: var(--bg);
|
||
border: 1px solid var(--surface);
|
||
border-radius: var(--radius);
|
||
padding: 8px;
|
||
position: relative;
|
||
}
|
||
#plot-canvas-wrap canvas { display: block; width: 100% !important; border-radius: 4px; }
|
||
|
||
/* FUNCTION LIST */
|
||
.fn-list { display: flex; flex-direction: column; gap: 6px; margin-bottom: 12px; }
|
||
.fn-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
background: var(--bg);
|
||
border: 1px solid var(--surface);
|
||
border-radius: var(--radius);
|
||
padding: 6px 10px;
|
||
font-size: 13px;
|
||
}
|
||
.fn-color { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; }
|
||
.fn-expr { flex: 1; color: var(--fg); }
|
||
.fn-remove { background: none; border: none; color: var(--comment); cursor: pointer; font-size: 16px; padding: 0 4px; line-height: 1; }
|
||
.fn-remove:hover { color: var(--red); }
|
||
|
||
/* EXOS PANEL */
|
||
.exo-list { display: flex; flex-direction: column; gap: 10px; }
|
||
.exo-card {
|
||
background: var(--bg);
|
||
border: 1px solid var(--surface);
|
||
border-radius: var(--radius);
|
||
padding: 14px;
|
||
}
|
||
.exo-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
|
||
.exo-title { font-size: 13px; font-weight: 600; color: var(--purple); }
|
||
.exo-tag {
|
||
font-size: 11px;
|
||
padding: 2px 8px;
|
||
border-radius: 20px;
|
||
border: 1px solid;
|
||
}
|
||
.tag-math { border-color: var(--cyan); color: var(--cyan); }
|
||
.tag-physique { border-color: var(--orange); color: var(--orange); }
|
||
.tag-cine { border-color: var(--green); color: var(--green); }
|
||
.exo-text { font-size: 13px; color: var(--fg2); margin-bottom: 10px; line-height: 1.7; }
|
||
.exo-hint { font-size: 12px; color: var(--comment); font-style: italic; margin-bottom: 10px; }
|
||
.corr-box {
|
||
background: var(--bg2);
|
||
border-left: 3px solid var(--green);
|
||
padding: 10px 12px;
|
||
border-radius: 0 var(--radius) var(--radius) 0;
|
||
font-size: 13px;
|
||
color: var(--green);
|
||
display: none;
|
||
white-space: pre-wrap;
|
||
}
|
||
.corr-box.visible { display: block; }
|
||
|
||
/* FORMULAIRE */
|
||
.formula-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
|
||
.formula-cat { margin-bottom: 16px; }
|
||
.formula-cat-title {
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
color: var(--comment);
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.7px;
|
||
margin-bottom: 8px;
|
||
padding-bottom: 6px;
|
||
border-bottom: 1px solid var(--surface);
|
||
}
|
||
.formula-item {
|
||
background: var(--bg);
|
||
border: 1px solid var(--surface);
|
||
border-radius: var(--radius);
|
||
padding: 10px 12px;
|
||
}
|
||
.formula-name { font-size: 11px; color: var(--comment); margin-bottom: 4px; }
|
||
.formula-expr { font-size: 14px; color: var(--yellow); font-weight: 500; }
|
||
.formula-unit { font-size: 11px; color: var(--cyan); margin-top: 2px; }
|
||
|
||
/* RANGE SLIDER */
|
||
input[type="range"] {
|
||
-webkit-appearance: none;
|
||
width: 100%;
|
||
height: 4px;
|
||
border-radius: 2px;
|
||
background: var(--surface2);
|
||
outline: none;
|
||
border: none;
|
||
padding: 0;
|
||
}
|
||
input[type="range"]::-webkit-slider-thumb {
|
||
-webkit-appearance: none;
|
||
width: 14px;
|
||
height: 14px;
|
||
border-radius: 50%;
|
||
background: var(--purple);
|
||
cursor: pointer;
|
||
}
|
||
input[type="range"]:focus { box-shadow: none; }
|
||
|
||
/* CONSTANTS TABLE */
|
||
.const-table { width: 100%; border-collapse: collapse; font-size: 13px; }
|
||
.const-table th { color: var(--comment); font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; padding: 6px 10px; text-align: left; border-bottom: 1px solid var(--surface); font-weight: 500; }
|
||
.const-table td { padding: 7px 10px; border-bottom: 1px solid var(--surface); }
|
||
.const-table tr:last-child td { border-bottom: none; }
|
||
.const-table td:first-child { color: var(--fg2); }
|
||
.const-table td:nth-child(2) { color: var(--yellow); font-weight: 500; }
|
||
.const-table td:nth-child(3) { color: var(--cyan); }
|
||
|
||
/* SCROLLBAR */
|
||
::-webkit-scrollbar { width: 6px; }
|
||
::-webkit-scrollbar-track { background: var(--bg2); }
|
||
::-webkit-scrollbar-thumb { background: var(--surface2); border-radius: 3px; }
|
||
|
||
/* RANGE DISPLAY */
|
||
.slider-wrap { display: flex; align-items: center; gap: 10px; }
|
||
.slider-val { min-width: 40px; text-align: right; font-size: 13px; color: var(--orange); }
|
||
|
||
.btn-row { display: flex; gap: 8px; flex-wrap: wrap; }
|
||
|
||
/* AI BADGE */
|
||
.ai-badge { font-size: 10px; background: rgba(189,147,249,0.15); border: 1px solid var(--purple); color: var(--purple); padding: 2px 7px; border-radius: 20px; margin-left: auto; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<header>
|
||
<div class="logo">Phys<span>Math</span></div>
|
||
<nav class="tabs">
|
||
<button class="tab active" onclick="showPage('plot')"><span class="dot"></span>Tracé</button>
|
||
<button class="tab" onclick="showPage('solver')"><span class="dot"></span>Solveur</button>
|
||
<button class="tab" onclick="showPage('exos')"><span class="dot"></span>Exercices</button>
|
||
<button class="tab" onclick="showPage('formules')"><span class="dot"></span>Formules</button>
|
||
</nav>
|
||
</header>
|
||
|
||
<!-- ══════════════ PAGE : TRACÉ ══════════════ -->
|
||
<div id="page-plot" class="page active">
|
||
<div class="two-col">
|
||
<div>
|
||
<div class="panel">
|
||
<div class="panel-header"><span class="accent" style="background:var(--cyan)"></span>Fonctions</div>
|
||
<div class="panel-body">
|
||
<div class="fn-list" id="fn-list"></div>
|
||
<div class="input-row" style="margin-bottom:8px">
|
||
<input type="text" id="fn-input" placeholder="ex: sin(x), x^2, exp(-x^2)" style="flex:1" />
|
||
</div>
|
||
<div class="btn-row">
|
||
<button class="btn btn-cyan" onclick="addFunction()">+ Ajouter</button>
|
||
<button class="btn btn-red" onclick="clearFunctions()">Tout effacer</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="panel" style="margin-top:16px">
|
||
<div class="panel-header"><span class="accent" style="background:var(--purple)"></span>Fenêtre d'affichage</div>
|
||
<div class="panel-body">
|
||
<div class="input-row">
|
||
<label>x min</label>
|
||
<input type="number" id="xmin" value="-10" style="width:80px">
|
||
<label style="margin-left:8px">x max</label>
|
||
<input type="number" id="xmax" value="10" style="width:80px">
|
||
</div>
|
||
<div class="input-row">
|
||
<label>y min</label>
|
||
<input type="number" id="ymin" value="-10" style="width:80px">
|
||
<label style="margin-left:8px">y max</label>
|
||
<input type="number" id="ymax" value="10" style="width:80px">
|
||
</div>
|
||
<div class="input-row" style="margin-bottom:0">
|
||
<label>Points</label>
|
||
<div class="slider-wrap" style="flex:1">
|
||
<input type="range" min="100" max="2000" step="50" value="600" id="pts-slider" oninput="document.getElementById('pts-val').textContent=this.value">
|
||
<span class="slider-val" id="pts-val">600</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="panel" style="margin-top:16px">
|
||
<div class="panel-header"><span class="accent" style="background:var(--green)"></span>Analyse</div>
|
||
<div class="panel-body">
|
||
<div class="input-row">
|
||
<label>f(x) à</label>
|
||
<input type="number" id="eval-x" value="0" style="width:80px">
|
||
<button class="btn btn-green" style="margin-left:8px" onclick="evalPoint()">Évaluer</button>
|
||
</div>
|
||
<div class="output" id="eval-output">—</div>
|
||
<div style="margin-top:12px">
|
||
<div class="input-row" style="margin-bottom:8px">
|
||
<label style="min-width:80px">Dérivée de</label>
|
||
<input type="text" id="deriv-fn" placeholder="x^3 - 2*x" style="flex:1">
|
||
</div>
|
||
<button class="btn btn-purple" onclick="computeDerivative()">Dériver symboliquement</button>
|
||
</div>
|
||
<div class="output info" id="deriv-output" style="margin-top:10px">—</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<div class="panel" style="height:fit-content">
|
||
<div class="panel-header"><span class="accent" style="background:var(--pink)"></span>Graphe</div>
|
||
<div class="panel-body" style="padding:10px">
|
||
<div id="plot-canvas-wrap">
|
||
<canvas id="plot-canvas" height="380"></canvas>
|
||
</div>
|
||
<div style="margin-top:10px; display:flex; gap:8px; justify-content:center">
|
||
<button class="btn btn-purple" onclick="drawPlot()">Tracer</button>
|
||
<button class="btn btn-cyan" onclick="autoScale()">Auto-zoom</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ══════════════ PAGE : SOLVEUR ══════════════ -->
|
||
<div id="page-solver" class="page">
|
||
<div class="two-col">
|
||
<div>
|
||
<div class="panel">
|
||
<div class="panel-header"><span class="accent" style="background:var(--green)"></span>Calculatrice avancée</div>
|
||
<div class="panel-body">
|
||
<div class="input-row">
|
||
<textarea id="calc-input" placeholder="sin(pi/4) sqrt(2) * exp(1) (3 + 4i) * (1 - 2i) factorial(10) log(100, 10) floor(3.7)"></textarea>
|
||
</div>
|
||
<div class="btn-row">
|
||
<button class="btn btn-green" onclick="calculate()">Calculer</button>
|
||
<button class="btn btn-red" onclick="document.getElementById('calc-input').value='';document.getElementById('calc-output').textContent='—';document.getElementById('calc-output').className='output'">Effacer</button>
|
||
</div>
|
||
<div class="output" id="calc-output" style="margin-top:12px">—</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="panel" style="margin-top:16px">
|
||
<div class="panel-header"><span class="accent" style="background:var(--orange)"></span>Zéros d'une fonction (dichotomie)</div>
|
||
<div class="panel-body">
|
||
<div class="input-row">
|
||
<label>f(x) =</label>
|
||
<input type="text" id="zero-fn" placeholder="x^3 - 2*x - 5" style="flex:1">
|
||
</div>
|
||
<div class="input-row">
|
||
<label>Intervalle</label>
|
||
<input type="number" id="zero-a" value="1" style="width:70px">
|
||
<span style="color:var(--comment);padding:0 6px">à</span>
|
||
<input type="number" id="zero-b" value="3" style="width:70px">
|
||
</div>
|
||
<div class="input-row" style="margin-bottom:8px">
|
||
<label>Précision</label>
|
||
<select id="zero-prec">
|
||
<option value="1e-6">10⁻⁶</option>
|
||
<option value="1e-8">10⁻⁸</option>
|
||
<option value="1e-10">10⁻¹⁰</option>
|
||
<option value="1e-4">10⁻⁴</option>
|
||
</select>
|
||
</div>
|
||
<button class="btn btn-orange" onclick="findZero()">Trouver le zéro</button>
|
||
<div class="output" id="zero-output" style="margin-top:12px">—</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<div class="panel">
|
||
<div class="panel-header"><span class="accent" style="background:var(--pink)"></span>Intégration numérique</div>
|
||
<div class="panel-body">
|
||
<div class="input-row">
|
||
<label>f(x) =</label>
|
||
<input type="text" id="int-fn" placeholder="sin(x), exp(-x^2), ..." style="flex:1">
|
||
</div>
|
||
<div class="input-row">
|
||
<label>De a =</label>
|
||
<input type="number" id="int-a" value="0" style="width:70px">
|
||
<label style="margin-left:8px">à b =</label>
|
||
<input type="number" id="int-b" value="3.14159" style="width:90px">
|
||
</div>
|
||
<button class="btn btn-pink" onclick="integrate()">Intégrer (Simpson)</button>
|
||
<div class="output" id="int-output" style="margin-top:12px">—</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="panel" style="margin-top:16px">
|
||
<div class="panel-header"><span class="accent" style="background:var(--cyan)"></span>Développement limité / Taylor</div>
|
||
<div class="panel-body">
|
||
<div class="input-row">
|
||
<label>f(x) =</label>
|
||
<input type="text" id="taylor-fn" placeholder="sin(x), exp(x), ln(1+x)..." style="flex:1">
|
||
</div>
|
||
<div class="input-row">
|
||
<label>Autour de</label>
|
||
<input type="number" id="taylor-pt" value="0" style="width:80px">
|
||
<label style="margin-left:8px">Ordre</label>
|
||
<input type="number" id="taylor-order" value="5" min="1" max="10" style="width:60px">
|
||
</div>
|
||
<button class="btn btn-cyan" onclick="taylorExpand()">Développer</button>
|
||
<div class="output info" id="taylor-output" style="margin-top:12px">—</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="panel" style="margin-top:16px">
|
||
<div class="panel-header"><span class="accent" style="background:var(--purple)"></span>Conversion unités physiques</div>
|
||
<div class="panel-body">
|
||
<div class="input-row">
|
||
<input type="number" id="conv-val" value="1" style="width:80px">
|
||
<input type="text" id="conv-expr" placeholder="60 km/h to m/s" style="flex:1; margin-left:8px">
|
||
</div>
|
||
<p style="font-size:11px;color:var(--comment);margin-bottom:8px">Ex: <code style="color:var(--yellow)">1 kg to g</code>, <code style="color:var(--yellow)">100 km/h to m/s</code>, <code style="color:var(--yellow)">1 atm to Pa</code></p>
|
||
<button class="btn btn-purple" onclick="convertUnit()">Convertir</button>
|
||
<div class="output" id="conv-output" style="margin-top:12px">—</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ══════════════ PAGE : EXERCICES ══════════════ -->
|
||
<div id="page-exos" class="page">
|
||
<div class="two-col">
|
||
<div>
|
||
<div class="panel">
|
||
<div class="panel-header"><span class="accent" style="background:var(--orange)"></span>Exercices guidés<span class="ai-badge">IA intégrée</span></div>
|
||
<div class="panel-body">
|
||
<div class="exo-list" id="exo-list">
|
||
<!-- injectés par JS -->
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<div class="panel">
|
||
<div class="panel-header"><span class="accent" style="background:var(--purple)"></span>Soumettre un exercice<span class="ai-badge">Claude</span></div>
|
||
<div class="panel-body">
|
||
<p style="font-size:12px;color:var(--comment);margin-bottom:12px">Colle l'énoncé d'un exercice de ton livre, Claude t'aide à le résoudre étape par étape.</p>
|
||
<textarea id="exo-input" placeholder="Énoncé de l'exercice..." style="min-height:120px;margin-bottom:10px"></textarea>
|
||
<div class="input-row" style="margin-bottom:10px">
|
||
<label>Matière</label>
|
||
<select id="exo-matiere">
|
||
<option>Mathématiques</option>
|
||
<option>Physique</option>
|
||
<option>Chimie</option>
|
||
</select>
|
||
</div>
|
||
<div class="btn-row">
|
||
<button class="btn btn-purple" onclick="solveExo()">Résoudre ↗</button>
|
||
<button class="btn btn-cyan" onclick="hintExo()">Juste un indice</button>
|
||
</div>
|
||
<div id="ai-output" style="margin-top:14px;font-size:13px;color:var(--fg2);line-height:1.8;display:none"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ══════════════ PAGE : FORMULES ══════════════ -->
|
||
<div id="page-formules" class="page">
|
||
<div class="two-col">
|
||
<div>
|
||
<div class="panel">
|
||
<div class="panel-header"><span class="accent" style="background:var(--yellow)"></span>Constantes fondamentales</div>
|
||
<div class="panel-body" style="padding:0 0 8px">
|
||
<table class="const-table">
|
||
<thead><tr><th>Constante</th><th>Valeur</th><th>Unité</th></tr></thead>
|
||
<tbody>
|
||
<tr><td>Vitesse de la lumière <em>c</em></td><td>2.998 × 10⁸</td><td>m/s</td></tr>
|
||
<tr><td>Constante de Planck <em>h</em></td><td>6.626 × 10⁻³⁴</td><td>J·s</td></tr>
|
||
<tr><td>Charge élémentaire <em>e</em></td><td>1.602 × 10⁻¹⁹</td><td>C</td></tr>
|
||
<tr><td>Masse de l'électron <em>mₑ</em></td><td>9.109 × 10⁻³¹</td><td>kg</td></tr>
|
||
<tr><td>Constante de Boltzmann <em>k</em></td><td>1.381 × 10⁻²³</td><td>J/K</td></tr>
|
||
<tr><td>Constante d'Avogadro <em>Nₐ</em></td><td>6.022 × 10²³</td><td>mol⁻¹</td></tr>
|
||
<tr><td>Constante des gaz <em>R</em></td><td>8.314</td><td>J/(mol·K)</td></tr>
|
||
<tr><td>Accélération gravité <em>g</em></td><td>9.807</td><td>m/s²</td></tr>
|
||
<tr><td>Perméabilité du vide <em>μ₀</em></td><td>4π × 10⁻⁷</td><td>H/m</td></tr>
|
||
<tr><td>Permittivité du vide <em>ε₀</em></td><td>8.854 × 10⁻¹²</td><td>F/m</td></tr>
|
||
<tr><td>Constante de Coulomb <em>k</em></td><td>8.988 × 10⁹</td><td>N·m²/C²</td></tr>
|
||
<tr><td>Constante gravitation <em>G</em></td><td>6.674 × 10⁻¹¹</td><td>m³/(kg·s²)</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<div class="panel">
|
||
<div class="panel-header"><span class="accent" style="background:var(--cyan)"></span>Formulaire physique</div>
|
||
<div class="panel-body">
|
||
|
||
<div class="formula-cat">
|
||
<div class="formula-cat-title">Mécanique</div>
|
||
<div class="formula-grid">
|
||
<div class="formula-item"><div class="formula-name">Équation horaire</div><div class="formula-expr">x(t) = x₀ + v₀t + ½at²</div></div>
|
||
<div class="formula-item"><div class="formula-name">Vitesse</div><div class="formula-expr">v(t) = v₀ + at</div></div>
|
||
<div class="formula-item"><div class="formula-name">Énergie cinétique</div><div class="formula-expr">Ec = ½mv²</div><div class="formula-unit">J</div></div>
|
||
<div class="formula-item"><div class="formula-name">Énergie potentielle</div><div class="formula-expr">Ep = mgh</div><div class="formula-unit">J</div></div>
|
||
<div class="formula-item"><div class="formula-name">2ème loi de Newton</div><div class="formula-expr">ΣF = ma</div><div class="formula-unit">N</div></div>
|
||
<div class="formula-item"><div class="formula-name">Moment cinétique</div><div class="formula-expr">p = mv</div><div class="formula-unit">kg·m/s</div></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="formula-cat">
|
||
<div class="formula-cat-title">Électricité</div>
|
||
<div class="formula-grid">
|
||
<div class="formula-item"><div class="formula-name">Loi d'Ohm</div><div class="formula-expr">U = RI</div></div>
|
||
<div class="formula-item"><div class="formula-name">Puissance</div><div class="formula-expr">P = UI = RI²</div><div class="formula-unit">W</div></div>
|
||
<div class="formula-item"><div class="formula-name">Condensateur</div><div class="formula-expr">q = Cu</div><div class="formula-unit">C</div></div>
|
||
<div class="formula-item"><div class="formula-name">Loi de Coulomb</div><div class="formula-expr">F = ke·q₁q₂/r²</div><div class="formula-unit">N</div></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="formula-cat">
|
||
<div class="formula-cat-title">Mathématiques</div>
|
||
<div class="formula-grid">
|
||
<div class="formula-item"><div class="formula-name">Binôme de Newton</div><div class="formula-expr">(a+b)ⁿ = Σ C(n,k)aᵏbⁿ⁻ᵏ</div></div>
|
||
<div class="formula-item"><div class="formula-name">Formule d'Euler</div><div class="formula-expr">eⁱˣ = cos(x) + i·sin(x)</div></div>
|
||
<div class="formula-item"><div class="formula-name">Dérivée produit</div><div class="formula-expr">(uv)' = u'v + uv'</div></div>
|
||
<div class="formula-item"><div class="formula-name">Intégration par parties</div><div class="formula-expr">∫uv' = [uv] - ∫u'v</div></div>
|
||
<div class="formula-item"><div class="formula-name">Suites géométriques</div><div class="formula-expr">Sn = a(1-rⁿ)/(1-r)</div></div>
|
||
<div class="formula-item"><div class="formula-name">Identités remarquables</div><div class="formula-expr">(a±b)² = a²±2ab+b²</div></div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
// ═══════════════════════════════════════════════
|
||
// NAVIGATION
|
||
// ═══════════════════════════════════════════════
|
||
function showPage(id) {
|
||
document.querySelectorAll('.page').forEach(p => p.classList.remove('active'));
|
||
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
|
||
document.getElementById('page-' + id).classList.add('active');
|
||
event.currentTarget.classList.add('active');
|
||
}
|
||
|
||
// ═══════════════════════════════════════════════
|
||
// TRACÉ DE FONCTIONS
|
||
// ═══════════════════════════════════════════════
|
||
let functions = [];
|
||
let plotChart = null;
|
||
const COLORS = ['#8be9fd','#50fa7b','#ff79c6','#ffb86c','#bd93f9','#f1fa8c','#ff5555','#8be9fd'];
|
||
|
||
function addFunction() {
|
||
const input = document.getElementById('fn-input');
|
||
const expr = input.value.trim();
|
||
if (!expr) return;
|
||
try {
|
||
math.compile(expr).evaluate({x: 0});
|
||
functions.push({ expr, color: COLORS[functions.length % COLORS.length] });
|
||
input.value = '';
|
||
renderFnList();
|
||
drawPlot();
|
||
} catch(e) {
|
||
showToast('Expression invalide: ' + e.message);
|
||
}
|
||
}
|
||
|
||
document.getElementById('fn-input').addEventListener('keydown', e => { if(e.key === 'Enter') addFunction(); });
|
||
|
||
function renderFnList() {
|
||
const list = document.getElementById('fn-list');
|
||
list.innerHTML = functions.map((f, i) => `
|
||
<div class="fn-item">
|
||
<span class="fn-color" style="background:${f.color}"></span>
|
||
<span class="fn-expr">f${i+1}(x) = ${f.expr}</span>
|
||
<button class="fn-remove" onclick="removeFunction(${i})">×</button>
|
||
</div>`).join('');
|
||
}
|
||
|
||
function removeFunction(i) {
|
||
functions.splice(i, 1);
|
||
renderFnList();
|
||
drawPlot();
|
||
}
|
||
|
||
function clearFunctions() {
|
||
functions = [];
|
||
renderFnList();
|
||
if(plotChart) { plotChart.destroy(); plotChart = null; }
|
||
}
|
||
|
||
function drawPlot() {
|
||
const xmin = parseFloat(document.getElementById('xmin').value);
|
||
const xmax = parseFloat(document.getElementById('xmax').value);
|
||
const ymin = parseFloat(document.getElementById('ymin').value);
|
||
const ymax = parseFloat(document.getElementById('ymax').value);
|
||
const pts = parseInt(document.getElementById('pts-slider').value);
|
||
|
||
const xs = [];
|
||
for(let i = 0; i <= pts; i++) xs.push(xmin + i*(xmax-xmin)/pts);
|
||
|
||
const datasets = functions.map(f => {
|
||
const compiled = math.compile(f.expr);
|
||
const ys = xs.map(x => {
|
||
try { const v = compiled.evaluate({x}); return (isFinite(v) && Math.abs(v) < 1e10) ? v : null; }
|
||
catch { return null; }
|
||
});
|
||
return {
|
||
label: `f(x) = ${f.expr}`,
|
||
data: xs.map((x,i) => ({x, y: ys[i]})),
|
||
borderColor: f.color,
|
||
backgroundColor: 'transparent',
|
||
borderWidth: 2,
|
||
pointRadius: 0,
|
||
tension: 0,
|
||
spanGaps: false
|
||
};
|
||
});
|
||
|
||
const ctx = document.getElementById('plot-canvas').getContext('2d');
|
||
if(plotChart) plotChart.destroy();
|
||
plotChart = new Chart(ctx, {
|
||
type: 'scatter',
|
||
data: { datasets },
|
||
options: {
|
||
animation: false,
|
||
responsive: true,
|
||
scales: {
|
||
x: {
|
||
type: 'linear', min: xmin, max: xmax,
|
||
grid: { color: '#44475a' },
|
||
ticks: { color: '#6272a4', font: { family: 'JetBrains Mono', size: 11 } },
|
||
border: { color: '#6272a4' }
|
||
},
|
||
y: {
|
||
type: 'linear', min: ymin, max: ymax,
|
||
grid: { color: '#44475a' },
|
||
ticks: { color: '#6272a4', font: { family: 'JetBrains Mono', size: 11 } },
|
||
border: { color: '#6272a4' }
|
||
}
|
||
},
|
||
plugins: {
|
||
legend: { labels: { color: '#f8f8f2', font: { family: 'JetBrains Mono', size: 12 }, boxWidth: 14, padding: 16 } },
|
||
tooltip: {
|
||
callbacks: {
|
||
label: ctx => `x=${ctx.raw.x.toFixed(4)}, y=${ctx.raw.y != null ? ctx.raw.y.toFixed(4) : 'N/A'}`
|
||
},
|
||
backgroundColor: '#282a36',
|
||
borderColor: '#6272a4',
|
||
borderWidth: 1,
|
||
titleColor: '#bd93f9',
|
||
bodyColor: '#f8f8f2',
|
||
bodyFont: { family: 'JetBrains Mono', size: 12 }
|
||
}
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
function autoScale() {
|
||
if(!functions.length) return;
|
||
const pts = 400;
|
||
const xmin = parseFloat(document.getElementById('xmin').value);
|
||
const xmax = parseFloat(document.getElementById('xmax').value);
|
||
let yvals = [];
|
||
for(let i=0;i<=pts;i++) {
|
||
const x = xmin + i*(xmax-xmin)/pts;
|
||
functions.forEach(f => {
|
||
try { const v = math.evaluate(f.expr, {x}); if(isFinite(v)) yvals.push(v); } catch{}
|
||
});
|
||
}
|
||
if(!yvals.length) return;
|
||
const ymin = Math.min(...yvals);
|
||
const ymax = Math.max(...yvals);
|
||
const margin = (ymax - ymin) * 0.1 || 1;
|
||
document.getElementById('ymin').value = (ymin - margin).toFixed(2);
|
||
document.getElementById('ymax').value = (ymax + margin).toFixed(2);
|
||
drawPlot();
|
||
}
|
||
|
||
function evalPoint() {
|
||
const x = parseFloat(document.getElementById('eval-x').value);
|
||
const out = document.getElementById('eval-output');
|
||
if(!functions.length) { out.textContent = 'Aucune fonction tracée.'; return; }
|
||
const results = functions.map((f,i) => {
|
||
try { const v = math.evaluate(f.expr, {x}); return `f${i+1}(${x}) = ${v.toFixed(8)}`; }
|
||
catch { return `f${i+1}(${x}) = erreur`; }
|
||
});
|
||
out.textContent = results.join('\n');
|
||
out.className = 'output';
|
||
}
|
||
|
||
function computeDerivative() {
|
||
const expr = document.getElementById('deriv-fn').value.trim();
|
||
const out = document.getElementById('deriv-output');
|
||
if(!expr) return;
|
||
try {
|
||
const d = math.derivative(expr, 'x');
|
||
const simplified = math.simplify(d);
|
||
out.textContent = `d/dx [${expr}] = ${simplified.toString()}`;
|
||
out.className = 'output info';
|
||
} catch(e) {
|
||
out.textContent = 'Erreur: ' + e.message;
|
||
out.className = 'output error';
|
||
}
|
||
}
|
||
|
||
// ═══════════════════════════════════════════════
|
||
// SOLVEUR
|
||
// ═══════════════════════════════════════════════
|
||
function calculate() {
|
||
const input = document.getElementById('calc-input').value.trim();
|
||
const out = document.getElementById('calc-output');
|
||
if(!input) return;
|
||
const lines = input.split('\n').filter(l => l.trim());
|
||
const results = [];
|
||
lines.forEach(line => {
|
||
try {
|
||
const res = math.evaluate(line.trim());
|
||
results.push(`${line.trim()} = ${res}`);
|
||
} catch(e) {
|
||
results.push(`${line.trim()} → Erreur`);
|
||
}
|
||
});
|
||
out.textContent = results.join('\n');
|
||
out.className = 'output';
|
||
}
|
||
|
||
function findZero() {
|
||
const fnStr = document.getElementById('zero-fn').value.trim();
|
||
let a = parseFloat(document.getElementById('zero-a').value);
|
||
let b = parseFloat(document.getElementById('zero-b').value);
|
||
const eps = parseFloat(document.getElementById('zero-prec').value);
|
||
const out = document.getElementById('zero-output');
|
||
if(!fnStr) return;
|
||
try {
|
||
const f = x => math.evaluate(fnStr, {x});
|
||
const fa = f(a), fb = f(b);
|
||
if(fa * fb > 0) { out.textContent = 'f(a) et f(b) doivent être de signes opposés.'; out.className='output error'; return; }
|
||
let iter = 0;
|
||
while(Math.abs(b-a) > eps && iter < 1000) {
|
||
const m = (a+b)/2;
|
||
if(f(m) * fa < 0) b = m; else a = m;
|
||
iter++;
|
||
}
|
||
const root = (a+b)/2;
|
||
out.textContent = `Racine ≈ ${root.toFixed(12)}\nf(racine) = ${f(root).toExponential(4)}\nIterations: ${iter}`;
|
||
out.className = 'output';
|
||
} catch(e) {
|
||
out.textContent = 'Erreur: ' + e.message;
|
||
out.className = 'output error';
|
||
}
|
||
}
|
||
|
||
function integrate() {
|
||
const fnStr = document.getElementById('int-fn').value.trim();
|
||
const a = parseFloat(document.getElementById('int-a').value);
|
||
const b = parseFloat(document.getElementById('int-b').value);
|
||
const out = document.getElementById('int-output');
|
||
if(!fnStr) return;
|
||
try {
|
||
const f = x => math.evaluate(fnStr, {x});
|
||
const n = 10000;
|
||
const h = (b-a)/n;
|
||
let sum = f(a) + f(b);
|
||
for(let i=1;i<n;i++) sum += (i%2===0?2:4)*f(a+i*h);
|
||
const result = sum * h/3;
|
||
out.textContent = `∫[${a}, ${b}] ${fnStr} dx ≈ ${result.toFixed(10)}\n(Méthode de Simpson, n=${n})`;
|
||
out.className = 'output';
|
||
} catch(e) {
|
||
out.textContent = 'Erreur: ' + e.message;
|
||
out.className = 'output error';
|
||
}
|
||
}
|
||
|
||
function taylorExpand() {
|
||
const fnStr = document.getElementById('taylor-fn').value.trim();
|
||
const a = parseFloat(document.getElementById('taylor-pt').value);
|
||
const order = parseInt(document.getElementById('taylor-order').value);
|
||
const out = document.getElementById('taylor-output');
|
||
if(!fnStr) return;
|
||
try {
|
||
let result = '';
|
||
let terms = [];
|
||
let node = math.parse(fnStr);
|
||
let coeff = math.evaluate(fnStr, {x: a});
|
||
let factorial = 1;
|
||
let derNode = node;
|
||
let fVal = coeff;
|
||
if(Math.abs(fVal) > 1e-12) terms.push(`${fVal.toFixed(5)}`);
|
||
for(let k=1; k<=order; k++) {
|
||
derNode = math.derivative(derNode, 'x');
|
||
factorial *= k;
|
||
const dval = math.evaluate(derNode.toString(), {x: a});
|
||
const c = dval / factorial;
|
||
if(Math.abs(c) > 1e-12) {
|
||
const sign = c>0 ? (terms.length?'+':'') : '-';
|
||
const absC = Math.abs(c).toFixed(5);
|
||
const xPart = a===0 ? `x${k>1?'^'+k:''}` : `(x-${a})${k>1?'^'+k:''}`;
|
||
terms.push(`${sign} ${absC}·${xPart}`);
|
||
}
|
||
}
|
||
out.textContent = `${fnStr} ≈ ${terms.join(' ') || '0'}\n(DL d'ordre ${order} en x=${a})`;
|
||
out.className = 'output info';
|
||
} catch(e) {
|
||
out.textContent = 'Erreur: ' + e.message;
|
||
out.className = 'output error';
|
||
}
|
||
}
|
||
|
||
function convertUnit() {
|
||
const expr = document.getElementById('conv-expr').value.trim();
|
||
const out = document.getElementById('conv-output');
|
||
if(!expr) return;
|
||
try {
|
||
const result = math.evaluate(expr);
|
||
out.textContent = `= ${result.toString()}`;
|
||
out.className = 'output';
|
||
} catch(e) {
|
||
out.textContent = 'Format: "100 km/h to m/s"\nErreur: ' + e.message;
|
||
out.className = 'output error';
|
||
}
|
||
}
|
||
|
||
// ═══════════════════════════════════════════════
|
||
// EXERCICES
|
||
// ═══════════════════════════════════════════════
|
||
const EXOS = [
|
||
{
|
||
titre: "Chute libre",
|
||
tag: "physique", tagClass: "tag-physique",
|
||
texte: "Un objet est lâché sans vitesse initiale depuis une hauteur h = 45 m. Quelle est sa vitesse à l'impact ? Au bout de combien de temps touche-t-il le sol ? (g = 9.8 m/s²)",
|
||
hint: "Utilise les équations horaires avec v₀ = 0. E_mec se conserve aussi.",
|
||
correction: `Équations horaires: h = ½gt², v = gt
|
||
|
||
t = √(2h/g) = √(2×45/9.8) = √9.18 ≈ 3.03 s
|
||
|
||
v = g·t = 9.8 × 3.03 ≈ 29.7 m/s
|
||
|
||
Vérification énergie: v = √(2gh) = √(2×9.8×45) = √882 ≈ 29.7 m/s ✓`
|
||
},
|
||
{
|
||
titre: "Dérivée et extremum",
|
||
tag: "math", tagClass: "tag-math",
|
||
texte: "Soit f(x) = x³ − 3x² + 2. Trouver les extrema locaux de f et déterminer s'il s'agit de maxima ou minima.",
|
||
hint: "Calculer f'(x), résoudre f'(x) = 0, étudier le signe de f'.",
|
||
correction: `f'(x) = 3x² - 6x = 3x(x - 2)
|
||
|
||
f'(x) = 0 ⟹ x = 0 ou x = 2
|
||
|
||
Signe de f':
|
||
x < 0 : f' > 0 (croissant)
|
||
0 < x < 2 : f' < 0 (décroissant)
|
||
x > 2 : f' > 0 (croissant)
|
||
|
||
→ Maximum local en x=0 : f(0) = 2
|
||
→ Minimum local en x=2 : f(2) = 8-12+2 = -2`
|
||
},
|
||
{
|
||
titre: "Circuit RC — charge",
|
||
tag: "physique", tagClass: "tag-physique",
|
||
texte: "Un condensateur C = 100 μF se charge à travers une résistance R = 10 kΩ sous une tension E = 5 V. Quelle est la tension aux bornes du condensateur après t = RC ?",
|
||
hint: "La loi de charge est u(t) = E(1 - e^(-t/RC)). τ = RC est la constante de temps.",
|
||
correction: `τ = RC = 10×10³ × 100×10⁻⁶ = 1 s
|
||
|
||
u(τ) = E(1 - e⁻¹) = 5 × (1 - 1/e)
|
||
= 5 × 0.6321...
|
||
≈ 3.16 V
|
||
|
||
À t = τ, le condensateur est chargé à ~63.2% de E.
|
||
À t = 5τ = 5s, il est chargé à ~99.3%`
|
||
},
|
||
{
|
||
titre: "Limite et continuité",
|
||
tag: "math", tagClass: "tag-math",
|
||
texte: "Calculer lim(x→0) [sin(3x) / (2x)] et lim(x→+∞) [(x² + 1) / (2x² - x + 3)].",
|
||
hint: "Pour la première, utiliser lim sin(u)/u = 1 quand u→0. Pour la seconde, diviser par x².",
|
||
correction: `Limite 1 : lim sin(3x)/(2x)
|
||
= lim [3x × sin(3x)/(3x)] / (2x)
|
||
= (3/2) × lim sin(3x)/(3x)
|
||
= 3/2 × 1 = 3/2
|
||
|
||
Limite 2 : lim (x²+1)/(2x²-x+3)
|
||
Diviser haut et bas par x²:
|
||
= lim (1 + 1/x²)/(2 - 1/x + 3/x²)
|
||
→ 1/2 quand x→+∞`
|
||
}
|
||
];
|
||
|
||
function renderExos() {
|
||
document.getElementById('exo-list').innerHTML = EXOS.map((e,i) => `
|
||
<div class="exo-card">
|
||
<div class="exo-header">
|
||
<span class="exo-title">${e.titre}</span>
|
||
<span class="exo-tag ${e.tagClass}">${e.tag}</span>
|
||
</div>
|
||
<div class="exo-text">${e.texte}</div>
|
||
<div class="exo-hint">💡 ${e.hint}</div>
|
||
<div class="btn-row" style="margin-bottom:8px">
|
||
<button class="btn btn-green" onclick="toggleCorr('corr-${i}')">Voir correction</button>
|
||
</div>
|
||
<div class="corr-box" id="corr-${i}">${e.correction}</div>
|
||
</div>`).join('');
|
||
}
|
||
|
||
function toggleCorr(id) {
|
||
const el = document.getElementById(id);
|
||
el.classList.toggle('visible');
|
||
}
|
||
|
||
async function solveExo() {
|
||
const input = document.getElementById('exo-input').value.trim();
|
||
const matiere = document.getElementById('exo-matiere').value;
|
||
const outDiv = document.getElementById('ai-output');
|
||
if(!input) return;
|
||
|
||
outDiv.style.display = 'block';
|
||
outDiv.innerHTML = '<span style="color:var(--comment)">Résolution en cours…</span>';
|
||
|
||
try {
|
||
const response = await fetch("https://api.anthropic.com/v1/messages", {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({
|
||
model: "claude-sonnet-4-20250514",
|
||
max_tokens: 1000,
|
||
system: `Tu es un professeur expert en ${matiere} pour lycée/prépa. Résous l'exercice étape par étape, clairement, en français. Utilise une notation mathématique claire. Donne le raisonnement complet, pas juste la réponse. Sois pédagogue.`,
|
||
messages: [{ role: "user", content: input }]
|
||
})
|
||
});
|
||
const data = await response.json();
|
||
const text = data.content?.find(b => b.type === 'text')?.text || 'Pas de réponse.';
|
||
outDiv.innerHTML = '<div style="color:var(--purple);font-size:11px;margin-bottom:8px;text-transform:uppercase;letter-spacing:0.5px">Correction</div>' + text.replace(/\n/g, '<br>').replace(/\*\*(.*?)\*\*/g, '<strong style="color:var(--yellow)">$1</strong>');
|
||
} catch(e) {
|
||
outDiv.innerHTML = '<span style="color:var(--red)">Erreur de connexion à l\'API.</span>';
|
||
}
|
||
}
|
||
|
||
async function hintExo() {
|
||
const input = document.getElementById('exo-input').value.trim();
|
||
const matiere = document.getElementById('exo-matiere').value;
|
||
const outDiv = document.getElementById('ai-output');
|
||
if(!input) return;
|
||
|
||
outDiv.style.display = 'block';
|
||
outDiv.innerHTML = '<span style="color:var(--comment)">Génération d\'un indice…</span>';
|
||
|
||
try {
|
||
const response = await fetch("https://api.anthropic.com/v1/messages", {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({
|
||
model: "claude-sonnet-4-20250514",
|
||
max_tokens: 1000,
|
||
system: `Tu es un professeur de ${matiere} pour lycée/prépa. Donne SEULEMENT un indice de démarrage sans résoudre, pour guider l'élève. Maximum 4 lignes, en français.`,
|
||
messages: [{ role: "user", content: input }]
|
||
})
|
||
});
|
||
const data = await response.json();
|
||
const text = data.content?.find(b => b.type === 'text')?.text || '';
|
||
outDiv.innerHTML = '<div style="color:var(--cyan);font-size:11px;margin-bottom:8px;text-transform:uppercase;letter-spacing:0.5px">Indice</div>' + text.replace(/\n/g, '<br>');
|
||
} catch(e) {
|
||
outDiv.innerHTML = '<span style="color:var(--red)">Erreur API.</span>';
|
||
}
|
||
}
|
||
|
||
// ═══════════════════════════════════════════════
|
||
// INIT
|
||
// ═══════════════════════════════════════════════
|
||
renderExos();
|
||
|
||
// Ajouter quelques fonctions exemples au démarrage
|
||
functions.push({ expr: 'sin(x)', color: COLORS[0] });
|
||
functions.push({ expr: 'cos(x)', color: COLORS[1] });
|
||
renderFnList();
|
||
drawPlot();
|
||
|
||
function showToast(msg) {
|
||
const t = document.createElement('div');
|
||
t.textContent = msg;
|
||
Object.assign(t.style, {
|
||
position:'fixed', bottom:'20px', right:'20px', background:'var(--red)',
|
||
color:'#fff', padding:'10px 16px', borderRadius:'8px', fontSize:'13px',
|
||
fontFamily:'var(--font-mono)', zIndex:9999, opacity:'0', transition:'opacity 0.2s'
|
||
});
|
||
document.body.appendChild(t);
|
||
requestAnimationFrame(() => t.style.opacity = '1');
|
||
setTimeout(() => { t.style.opacity='0'; setTimeout(()=>t.remove(),200); }, 3000);
|
||
}
|
||
|
||
document.getElementById('calc-input').addEventListener('keydown', e => {
|
||
if(e.ctrlKey && e.key === 'Enter') calculate();
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|