Files
Site-Web/test.html
T
2026-05-16 11:10:19 +02:00

1100 lines
43 KiB
HTML
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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)&#10;sqrt(2) * exp(1)&#10;(3 + 4i) * (1 - 2i)&#10;factorial(10)&#10;log(100, 10)&#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>× 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>