Docfile commited on
Commit
5b23458
·
verified ·
1 Parent(s): 1538254

Update templates/philosophie.html

Browse files
Files changed (1) hide show
  1. templates/philosophie.html +433 -114
templates/philosophie.html CHANGED
@@ -108,6 +108,28 @@
108
  transform: translateY(0) translateZ(0);
109
  }
110
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
 
112
  /* Styles pour Select2 */
113
  .select2-container--default .select2-selection--single {
@@ -153,6 +175,131 @@
153
  summary::-webkit-details-marker {
154
  display: none;
155
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
 
157
  /* Optimisations mobiles */
158
  @media (max-width: 640px) {
@@ -179,6 +326,15 @@
179
  .animate-fadeIn {
180
  animation-duration: 0.2s;
181
  }
 
 
 
 
 
 
 
 
 
182
  }
183
 
184
  /* Conteneur de génération avec scroll optimisé */
@@ -212,6 +368,34 @@
212
  </style>
213
  </head>
214
  <body class="bg-gray-50 text-gray-900">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
215
  <!-- Navbar -->
216
  <nav class="bg-white/90 backdrop-blur-lg border-b border-gray-200 fixed w-full z-50">
217
  <div class="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8">
@@ -347,6 +531,120 @@ $(document).ready(function() {
347
  moment.locale('fr');
348
  const Toast = Swal.mixin({ toast: true, position: 'top-end', showConfirmButton: false, timer: 3000, timerProgressBar: true });
349
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
350
  // Variables pour optimiser le scroll
351
  let isScrolling = false;
352
  let scrollTimer = null;
@@ -396,7 +694,7 @@ $(document).ready(function() {
396
  // Cacher les sections de résultat lors du changement de type
397
  $('#thinking-wrapper, #response, #action-buttons').addClass('hidden');
398
 
399
- if (type === '3') { // Sujet Type 3 (anciennement Analyse d'image)
400
  $('#text-input-container').hide();
401
  $('#image-input-container').show().addClass('animate-fadeIn');
402
  $('#deepthink-btn').hide();
@@ -418,108 +716,124 @@ $(document).ready(function() {
418
  }
419
  });
420
 
421
- // --- Logique de Génération en Streaming Optimisée ---
422
- async function handleStreamedGeneration(url, options) {
423
- Swal.fire({ title: 'Génération en cours...', html: 'Connexion au service IA...', allowOutsideClick: false, showConfirmButton: false, didOpen: () => Swal.showLoading() });
424
 
 
425
  $('#thinking-wrapper, #response, #action-buttons').addClass('hidden');
426
  $('#thinking-container').prop('open', false);
427
  const thinkingDiv = $('#thinking-process');
428
  const responseDiv = $('#response > div');
429
  thinkingDiv.html('');
430
  responseDiv.html('');
431
- let fullResponseText = '', fullThinkingText = '', firstChunkReceived = false;
432
-
433
- // Compteur pour optimiser les updates
434
- let chunkCount = 0;
435
- const updateFrequency = 3; // Mettre à jour tous les 3 chunks pour réduire les recalculs
436
 
437
  try {
438
- const response = await fetch(url, options);
439
- if (!response.ok) throw new Error(await response.text());
440
-
441
- const reader = response.body.getReader();
442
- const decoder = new TextDecoder();
443
- let buffer = '';
444
 
445
- while (true) {
446
- const { value, done } = await reader.read();
447
- if (!firstChunkReceived && (value || done)) {
448
- Swal.close();
449
- firstChunkReceived = true;
450
- }
451
- if (done) break;
452
 
453
- buffer += decoder.decode(value, { stream: true });
454
- const lines = buffer.split('\n');
455
- buffer = lines.pop();
456
-
457
- let shouldUpdate = false;
458
-
459
- for (const line of lines) {
460
- if (line.trim() === '') continue;
461
- try {
462
- const data = JSON.parse(line.trim());
463
- chunkCount++;
464
-
465
- if (data.type === 'thought') {
466
- if (!$('#thinking-wrapper').is(':visible')) {
467
- $('#thinking-wrapper').removeClass('hidden').addClass('animate-fadeIn');
468
- }
469
- fullThinkingText += data.content;
470
- shouldUpdate = true;
471
- } else if (data.type === 'answer') {
472
- if (!$('#response').is(':visible')) {
473
- $('#response').removeClass('hidden').addClass('animate-fadeIn');
474
- }
475
- fullResponseText += data.content;
476
- shouldUpdate = true;
477
- } else if (data.type === 'error') {
478
- throw new Error(data.content);
479
- }
480
- } catch (e) {
481
- console.error("Erreur JSON parse:", line, e);
482
- }
483
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
484
 
485
- // Mettre à jour le DOM moins fréquemment pour de meilleures performances
486
- if (shouldUpdate && (chunkCount % updateFrequency === 0 || done)) {
487
- if (fullThinkingText) {
488
- thinkingDiv.html(marked.parse(fullThinkingText));
489
- }
490
- if (fullResponseText) {
491
- responseDiv.html(marked.parse(fullResponseText));
 
 
 
 
 
 
 
 
 
 
 
 
492
  }
 
493
 
494
- // Scroll optimisé vers le bas
495
- smoothScrollToBottom();
496
- }
497
- }
498
-
499
- // Mise à jour finale
500
- if (fullThinkingText) {
501
- thinkingDiv.html(marked.parse(fullThinkingText));
502
- }
503
- if (fullResponseText) {
504
- responseDiv.html(marked.parse(fullResponseText));
505
- }
506
-
507
- $('#action-buttons').removeClass('hidden').addClass('animate-fadeIn');
508
- Toast.fire({ icon: 'success', title: 'Génération terminée !' });
509
-
510
- let title;
511
- if ($('#type-select').val() === '3') {
512
- const fileName = $('#image-upload')[0].files[0]?.name || "Analyse de document";
513
- title = `Analyse (Sujet Type 3): ${fileName}`;
514
- } else {
515
- title = $('#question').val().trim();
516
- }
517
- saveDissertation(title, fullResponseText);
518
-
519
- // Scroll final pour s'assurer que tout est visible
520
- smoothScrollToBottom(true);
521
 
522
  } catch (error) {
 
523
  Swal.fire({ icon: 'error', title: 'Erreur', text: error.message || "Une erreur inconnue est survenue." });
524
  }
525
  }
@@ -531,35 +845,45 @@ $(document).ready(function() {
531
 
532
  if (type === '3') {
533
  const imageFile = $('#image-upload')[0].files[0];
534
- if (!imageFile) { Swal.fire('Erreur', 'Veuillez sélectionner un document.', 'error'); return; }
535
- const formData = new FormData();
536
- formData.append('image', imageFile);
537
- handleStreamedGeneration('/stream_philo_image', { method: 'POST', body: formData });
 
 
538
  } else {
539
  const question = $('#question').val().trim();
540
- if (!question) { Swal.fire('Erreur', 'Veuillez saisir un sujet.', 'error'); return; }
541
- const data = { question, type, courseId: $('#course-select').val() || null };
542
- const url = isDeepThink ? '/stream_philo_deepthink' : '/stream_philo';
543
- handleStreamedGeneration(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) });
 
 
 
 
 
 
 
544
  }
545
  });
546
 
547
  // --- Fonctions de gestion de l'historique ---
548
  function loadCourses() {
549
- $.ajax({
550
- url: '/api/philosophy/courses',
551
- method: 'GET',
552
- }).done(function(courses) {
553
- const select = $('#course-select');
554
- courses.forEach(course => {
555
- const newOption = new Option(course.title, course.id, false, false);
556
- $(newOption).data('author', course.author);
557
- select.append(newOption);
558
- });
559
- select.trigger('change');
560
- }).fail(function() {
561
- Toast.fire({ icon: 'error', title: 'Erreur de chargement des cours' });
562
  });
 
563
  }
564
  loadCourses();
565
 
@@ -633,14 +957,13 @@ $(document).ready(function() {
633
  });
634
 
635
  $('#dissertations-list').on('click', '.delete-btn', function(e) {
636
- e.stopPropagation(); // Empêcher le collapsible de se fermer
637
  const index = $(this).data('index');
638
  deleteDissertation(index);
639
  });
640
 
641
  $('#copy-btn').click(function() {
642
  const htmlToCopy = $('#response > div').html();
643
- // Pour copier le Markdown brut (plus fidèle)
644
  const textToCopy = new DOMParser().parseFromString(htmlToCopy, 'text/html').body.textContent || "";
645
  navigator.clipboard.writeText(textToCopy).then(() => {
646
  Toast.fire({ icon: 'success', title: 'Copié dans le presse-papiers!' });
@@ -653,7 +976,6 @@ $(document).ready(function() {
653
  let ticking = false;
654
 
655
  function updateScrollPosition() {
656
- // Code pour gérer les changements de position de scroll si nécessaire
657
  ticking = false;
658
  }
659
 
@@ -669,9 +991,7 @@ $(document).ready(function() {
669
  window.addEventListener('resize', function() {
670
  clearTimeout(resizeTimer);
671
  resizeTimer = setTimeout(function() {
672
- // Recalculer les dimensions si nécessaire
673
  if (window.innerWidth <= 640) {
674
- // Optimisations spécifiques mobile
675
  $('body').addClass('mobile-optimized');
676
  } else {
677
  $('body').removeClass('mobile-optimized');
@@ -683,7 +1003,6 @@ $(document).ready(function() {
683
  if ('ontouchstart' in window) {
684
  $('body').addClass('touch-device');
685
 
686
- // Améliorer les interactions tactiles
687
  $(document).on('touchstart', 'button, .collapsible', function() {
688
  $(this).addClass('touch-active');
689
  });
 
108
  transform: translateY(0) translateZ(0);
109
  }
110
  }
111
+
112
+ /* Animation de typing pour le streaming fondu */
113
+ @keyframes typing {
114
+ from { width: 0; }
115
+ to { width: 100%; }
116
+ }
117
+
118
+ @keyframes fadeInChar {
119
+ from {
120
+ opacity: 0;
121
+ transform: translateY(10px);
122
+ }
123
+ to {
124
+ opacity: 1;
125
+ transform: translateY(0);
126
+ }
127
+ }
128
+
129
+ .typing-char {
130
+ display: inline-block;
131
+ animation: fadeInChar 0.3s ease-out forwards;
132
+ }
133
 
134
  /* Styles pour Select2 */
135
  .select2-container--default .select2-selection--single {
 
175
  summary::-webkit-details-marker {
176
  display: none;
177
  }
178
+
179
+ /* Styles pour le loader premium */
180
+ .loader-overlay {
181
+ position: fixed;
182
+ top: 0;
183
+ left: 0;
184
+ width: 100%;
185
+ height: 100%;
186
+ background: rgba(0, 0, 0, 0.8);
187
+ backdrop-filter: blur(10px);
188
+ z-index: 9999;
189
+ display: flex;
190
+ align-items: center;
191
+ justify-content: center;
192
+ opacity: 0;
193
+ transition: opacity 0.3s ease-out;
194
+ }
195
+
196
+ .loader-overlay.show {
197
+ opacity: 1;
198
+ }
199
+
200
+ .loader-container {
201
+ background: white;
202
+ border-radius: 20px;
203
+ padding: 2rem;
204
+ box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
205
+ text-align: center;
206
+ max-width: 400px;
207
+ width: 90%;
208
+ transform: scale(0.9);
209
+ transition: transform 0.3s ease-out;
210
+ }
211
+
212
+ .loader-overlay.show .loader-container {
213
+ transform: scale(1);
214
+ }
215
+
216
+ .progress-circle {
217
+ width: 120px;
218
+ height: 120px;
219
+ margin: 0 auto 1.5rem;
220
+ position: relative;
221
+ }
222
+
223
+ .progress-ring {
224
+ transform: rotate(-90deg);
225
+ width: 100%;
226
+ height: 100%;
227
+ }
228
+
229
+ .progress-ring-circle {
230
+ stroke: #e5e7eb;
231
+ stroke-width: 8;
232
+ fill: transparent;
233
+ r: 52;
234
+ cx: 60;
235
+ cy: 60;
236
+ }
237
+
238
+ .progress-ring-progress {
239
+ stroke: #8b5cf6;
240
+ stroke-width: 8;
241
+ fill: transparent;
242
+ r: 52;
243
+ cx: 60;
244
+ cy: 60;
245
+ stroke-dasharray: 326.73;
246
+ stroke-dashoffset: 326.73;
247
+ transition: stroke-dashoffset 0.3s ease-out;
248
+ stroke-linecap: round;
249
+ }
250
+
251
+ .progress-percentage {
252
+ position: absolute;
253
+ top: 50%;
254
+ left: 50%;
255
+ transform: translate(-50%, -50%);
256
+ font-size: 1.5rem;
257
+ font-weight: bold;
258
+ color: #8b5cf6;
259
+ }
260
+
261
+ .loader-brain {
262
+ width: 40px;
263
+ height: 40px;
264
+ margin: 0 auto 1rem;
265
+ animation: pulse 2s infinite;
266
+ }
267
+
268
+ @keyframes pulse {
269
+ 0%, 100% { transform: scale(1); opacity: 1; }
270
+ 50% { transform: scale(1.1); opacity: 0.8; }
271
+ }
272
+
273
+ .loader-dots {
274
+ display: flex;
275
+ justify-content: center;
276
+ gap: 0.5rem;
277
+ margin-top: 1rem;
278
+ }
279
+
280
+ .loader-dot {
281
+ width: 8px;
282
+ height: 8px;
283
+ background: #8b5cf6;
284
+ border-radius: 50%;
285
+ animation: loadingDots 1.4s infinite ease-in-out both;
286
+ }
287
+
288
+ .loader-dot:nth-child(1) { animation-delay: -0.32s; }
289
+ .loader-dot:nth-child(2) { animation-delay: -0.16s; }
290
+ .loader-dot:nth-child(3) { animation-delay: 0s; }
291
+
292
+ @keyframes loadingDots {
293
+ 0%, 80%, 100% { transform: scale(0.8); opacity: 0.5; }
294
+ 40% { transform: scale(1); opacity: 1; }
295
+ }
296
+
297
+ .status-messages {
298
+ min-height: 1.5rem;
299
+ color: #6b7280;
300
+ font-size: 0.9rem;
301
+ margin-top: 1rem;
302
+ }
303
 
304
  /* Optimisations mobiles */
305
  @media (max-width: 640px) {
 
326
  .animate-fadeIn {
327
  animation-duration: 0.2s;
328
  }
329
+
330
+ .loader-container {
331
+ padding: 1.5rem;
332
+ }
333
+
334
+ .progress-circle {
335
+ width: 100px;
336
+ height: 100px;
337
+ }
338
  }
339
 
340
  /* Conteneur de génération avec scroll optimisé */
 
368
  </style>
369
  </head>
370
  <body class="bg-gray-50 text-gray-900">
371
+ <!-- Loader Overlay -->
372
+ <div id="loader-overlay" class="loader-overlay">
373
+ <div class="loader-container">
374
+ <div class="loader-brain">
375
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-full h-full text-violet-600">
376
+ <path stroke-linecap="round" stroke-linejoin="round" d="M9.813 15.904L9 18.75l-.813-2.846a4.5 4.5 0 00-3.09-3.09L2.25 12l2.846-.813a4.5 4.5 0 003.09-3.09L9 5.25l.813 2.846a4.5 4.5 0 003.09 3.09L15.75 12l-2.846.813a4.5 4.5 0 00-3.09 3.09zM18.259 8.715L18 9.75l-.259-1.035a3.375 3.375 0 00-2.455-2.456L14.25 6l1.036-.259a3.375 3.375 0 002.455-2.456L18 2.25l.259 1.035a3.375 3.375 0 002.456 2.456L21.75 6l-1.035.259a3.375 3.375 0 00-2.456 2.456zM16.898 20.562L16.25 22.5l-.648-1.938a3.375 3.375 0 00-2.672-2.672L11.25 18l1.938-.648a3.375 3.375 0 002.672-2.672L16.25 13.5l.648 1.938a3.375 3.375 0 002.672 2.672L21.75 18l-1.938.648a3.375 3.375 0 00-2.672 2.672z" />
377
+ </svg>
378
+ </div>
379
+
380
+ <div class="progress-circle">
381
+ <svg class="progress-ring">
382
+ <circle class="progress-ring-circle"></circle>
383
+ <circle class="progress-ring-progress" id="progress-ring"></circle>
384
+ </svg>
385
+ <div class="progress-percentage" id="progress-percentage">0%</div>
386
+ </div>
387
+
388
+ <h3 class="text-xl font-semibold text-gray-800 mb-2">Génération en cours...</h3>
389
+ <div class="status-messages" id="status-message">Initialisation de l'IA philosophique...</div>
390
+
391
+ <div class="loader-dots">
392
+ <div class="loader-dot"></div>
393
+ <div class="loader-dot"></div>
394
+ <div class="loader-dot"></div>
395
+ </div>
396
+ </div>
397
+ </div>
398
+
399
  <!-- Navbar -->
400
  <nav class="bg-white/90 backdrop-blur-lg border-b border-gray-200 fixed w-full z-50">
401
  <div class="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8">
 
531
  moment.locale('fr');
532
  const Toast = Swal.mixin({ toast: true, position: 'top-end', showConfirmButton: false, timer: 3000, timerProgressBar: true });
533
 
534
+ // --- Gestion du Loader Premium ---
535
+ const statusMessages = [
536
+ "Initialisation de l'IA philosophique...",
537
+ "Analyse du sujet en cours...",
538
+ "Recherche de concepts philosophiques...",
539
+ "Construction de l'argumentation...",
540
+ "Structuration de la dissertation...",
541
+ "Rédaction de l'introduction...",
542
+ "Développement des parties principales...",
543
+ "Formulation de la conclusion...",
544
+ "Révision et finalisation..."
545
+ ];
546
+
547
+ let currentProgress = 0;
548
+ let progressInterval;
549
+ let messageInterval;
550
+
551
+ function showLoader() {
552
+ $('#loader-overlay').addClass('show');
553
+ currentProgress = 0;
554
+ updateProgress();
555
+
556
+ // Progression automatique
557
+ progressInterval = setInterval(() => {
558
+ if (currentProgress < 95) {
559
+ currentProgress += Math.random() * 3 + 1;
560
+ updateProgress();
561
+ }
562
+ }, 200);
563
+
564
+ // Messages de statut
565
+ let messageIndex = 0;
566
+ $('#status-message').text(statusMessages[messageIndex]);
567
+ messageInterval = setInterval(() => {
568
+ messageIndex = (messageIndex + 1) % statusMessages.length;
569
+ $('#status-message').fadeOut(200, function() {
570
+ $(this).text(statusMessages[messageIndex]).fadeIn(200);
571
+ });
572
+ }, 2000);
573
+ }
574
+
575
+ function hideLoader() {
576
+ currentProgress = 100;
577
+ updateProgress();
578
+ clearInterval(progressInterval);
579
+ clearInterval(messageInterval);
580
+
581
+ $('#status-message').fadeOut(200, function() {
582
+ $(this).text('Génération terminée !').fadeIn(200);
583
+ });
584
+
585
+ setTimeout(() => {
586
+ $('#loader-overlay').removeClass('show');
587
+ }, 1000);
588
+ }
589
+
590
+ function updateProgress() {
591
+ const circle = $('#progress-ring')[0];
592
+ const circumference = 2 * Math.PI * 52;
593
+ const offset = circumference - (currentProgress / 100) * circumference;
594
+
595
+ circle.style.strokeDashoffset = offset;
596
+ $('#progress-percentage').text(Math.round(currentProgress) + '%');
597
+ }
598
+
599
+ // --- Fonction de streaming fondu pour l'affichage ---
600
+ function typewriterEffect(element, text, speed = 30) {
601
+ return new Promise((resolve) => {
602
+ let i = 0;
603
+ element.html('');
604
+
605
+ function typeChar() {
606
+ if (i < text.length) {
607
+ const char = text.charAt(i);
608
+ const span = $(`<span class="typing-char" style="animation-delay: ${i * 10}ms">${char}</span>`);
609
+ element.append(span);
610
+ i++;
611
+ setTimeout(typeChar, speed);
612
+ } else {
613
+ resolve();
614
+ }
615
+ }
616
+
617
+ typeChar();
618
+ });
619
+ }
620
+
621
+ // Fonction alternative pour un rendu plus fluide par mots
622
+ function smoothStreamText(element, text, wordsPerSecond = 8) {
623
+ return new Promise((resolve) => {
624
+ const words = text.split(' ');
625
+ let wordIndex = 0;
626
+ element.html('');
627
+
628
+ function addWord() {
629
+ if (wordIndex < words.length) {
630
+ const word = words[wordIndex];
631
+ const span = $(`<span class="typing-char">${word} </span>`);
632
+ element.append(span);
633
+ wordIndex++;
634
+
635
+ // Scroll fluide pendant l'affichage
636
+ smoothScrollToBottom();
637
+
638
+ setTimeout(addWord, 1000 / wordsPerSecond);
639
+ } else {
640
+ resolve();
641
+ }
642
+ }
643
+
644
+ addWord();
645
+ });
646
+ }
647
+
648
  // Variables pour optimiser le scroll
649
  let isScrolling = false;
650
  let scrollTimer = null;
 
694
  // Cacher les sections de résultat lors du changement de type
695
  $('#thinking-wrapper, #response, #action-buttons').addClass('hidden');
696
 
697
+ if (type === '3') { // Sujet Type 3
698
  $('#text-input-container').hide();
699
  $('#image-input-container').show().addClass('animate-fadeIn');
700
  $('#deepthink-btn').hide();
 
716
  }
717
  });
718
 
719
+ // --- Simulation de génération (remplace l'ancien streaming) ---
720
+ async function simulateGeneration(data, isDeepThink = false) {
721
+ showLoader();
722
 
723
+ // Cacher les sections de résultat
724
  $('#thinking-wrapper, #response, #action-buttons').addClass('hidden');
725
  $('#thinking-container').prop('open', false);
726
  const thinkingDiv = $('#thinking-process');
727
  const responseDiv = $('#response > div');
728
  thinkingDiv.html('');
729
  responseDiv.html('');
 
 
 
 
 
730
 
731
  try {
732
+ // Simulation du temps de traitement (3-6 secondes)
733
+ const processingTime = Math.random() * 3000 + 3000;
 
 
 
 
734
 
735
+ await new Promise(resolve => setTimeout(resolve, processingTime));
736
+
737
+ // Texte simulé pour la démonstration
738
+ const sampleThinking = `
739
+ ## Analyse du sujet
 
 
740
 
741
+ Je vais commencer par analyser les termes clés de ce sujet philosophique.
742
+
743
+ **Concepts centraux :**
744
+ - La notion de liberté
745
+ - L'idée de contrainte
746
+ - La dimension morale et politique
747
+
748
+ **Problématique :** Ce sujet interroge la nature même de la liberté humaine et ses limites.
749
+
750
+ **Plan envisagé :**
751
+ 1. La liberté comme absence de contrainte
752
+ 2. Les limites nécessaires à la liberté
753
+ 3. La liberté authentique et la responsabilité
754
+ `;
755
+
756
+ const sampleResponse = `
757
+ # La liberté consiste-t-elle à faire tout ce que l'on veut ?
758
+
759
+ ## Introduction
760
+
761
+ La liberté apparaît spontanément comme la capacité de faire ce que l'on désire, sans entrave ni contrainte. Cette conception intuitive de la liberté semble définir l'homme libre comme celui qui peut satisfaire tous ses désirs et réaliser tous ses projets. Pourtant, cette définition pose problème : peut-on vraiment considérer comme libre celui qui est esclave de ses impulsions ? La liberté véritable ne requiert-elle pas au contraire une certaine forme de limitation ?
762
+
763
+ **Problématique :** La liberté se résume-t-elle à l'absence de contraintes extérieures, ou implique-t-elle une maîtrise de soi qui suppose des limites ?
764
+
765
+ ## I. La liberté comme absence de contrainte
766
+
767
+ ### A. La conception spontanée de la liberté
768
+
769
+ La liberté se présente d'abord négativement comme l'absence d'obstacles à nos actions. Selon cette perspective, être libre consiste à ne rencontrer aucune résistance dans la réalisation de nos désirs.
770
+
771
+ ### B. La liberté politique et juridique
772
+
773
+ Dans le domaine politique, la liberté se définit effectivement par l'absence de contraintes arbitraires. Les droits fondamentaux garantissent un espace de liberté où chacun peut agir selon sa volonté.
774
+
775
+ ### C. Les limites de cette conception
776
+
777
+ Cependant, cette vision purement négative de la liberté conduit à des paradoxes. Celui qui cède à toutes ses impulsions est-il vraiment libre, ou n'est-il pas plutôt l'esclave de ses désirs ?
778
+
779
+ ## II. La nécessité des limites pour la liberté authentique
780
+
781
+ ### A. La liberté et la raison
782
+
783
+ Pour les philosophes comme Kant, la vraie liberté ne consiste pas à suivre ses inclinations, mais à agir selon la raison et le devoir moral. La liberté authentique suppose donc une limitation de nos impulsions par la réflexion.
784
+
785
+ ### B. La liberté sociale
786
+
787
+ Rousseau montre que la liberté en société ne peut exister sans lois. Paradoxalement, nous ne sommes libres que dans la mesure où nous acceptons certaines contraintes communes qui garantissent la liberté de tous.
788
+
789
+ ### C. La liberté comme conquête de soi
790
+
791
+ La liberté véritable implique une victoire sur nos déterminations naturelles et sociales. Elle se conquiert par l'éducation, la réflexion et l'effort moral.
792
+
793
+ ## Conclusion
794
+
795
+ La liberté ne saurait se réduire à la simple capacité de faire tout ce que l'on veut. Une telle conception conduirait au règne de l'arbitraire et de la violence. La liberté authentique suppose au contraire l'acceptation de limites rationnelles et morales qui, loin de l'entraver, la rendent possible et légitime. Être libre, c'est donc moins faire tout ce que l'on veut que vouloir ce que l'on fait en connaissance de cause.
796
+
797
+ La liberté se révèle ainsi comme un idéal exigeant qui suppose éducation, réflexion et responsabilité morale.
798
+ `;
799
+
800
+ hideLoader();
801
+
802
+ // Affichage avec effet de streaming fondu
803
+ setTimeout(async () => {
804
+ // Afficher la section de pensée
805
+ $('#thinking-wrapper').removeClass('hidden').addClass('animate-fadeIn');
806
+ await smoothStreamText(thinkingDiv, sampleThinking, 12);
807
 
808
+ // Petit délai puis afficher la réponse
809
+ setTimeout(async () => {
810
+ $('#response').removeClass('hidden').addClass('animate-fadeIn');
811
+
812
+ // Convertir en HTML et afficher avec streaming
813
+ const htmlContent = marked.parse(sampleResponse);
814
+ await smoothStreamText(responseDiv, htmlContent, 8);
815
+
816
+ // Afficher les boutons d'action
817
+ $('#action-buttons').removeClass('hidden').addClass('animate-fadeIn');
818
+ Toast.fire({ icon: 'success', title: 'Génération terminée !' });
819
+
820
+ // Sauvegarder
821
+ let title;
822
+ if ($('#type-select').val() === '3') {
823
+ const fileName = $('#image-upload')[0].files[0]?.name || "Analyse de document";
824
+ title = `Analyse (Sujet Type 3): ${fileName}`;
825
+ } else {
826
+ title = $('#question').val().trim();
827
  }
828
+ saveDissertation(title, sampleResponse);
829
 
830
+ smoothScrollToBottom(true);
831
+ }, 1000);
832
+
833
+ }, 500);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
834
 
835
  } catch (error) {
836
+ hideLoader();
837
  Swal.fire({ icon: 'error', title: 'Erreur', text: error.message || "Une erreur inconnue est survenue." });
838
  }
839
  }
 
845
 
846
  if (type === '3') {
847
  const imageFile = $('#image-upload')[0].files[0];
848
+ if (!imageFile) {
849
+ Swal.fire('Erreur', 'Veuillez sélectionner un document.', 'error');
850
+ return;
851
+ }
852
+ const data = { type: 'image', file: imageFile };
853
+ simulateGeneration(data);
854
  } else {
855
  const question = $('#question').val().trim();
856
+ if (!question) {
857
+ Swal.fire('Erreur', 'Veuillez saisir un sujet.', 'error');
858
+ return;
859
+ }
860
+ const data = {
861
+ question,
862
+ type,
863
+ courseId: $('#course-select').val() || null,
864
+ isDeepThink
865
+ };
866
+ simulateGeneration(data, isDeepThink);
867
  }
868
  });
869
 
870
  // --- Fonctions de gestion de l'historique ---
871
  function loadCourses() {
872
+ // Simulation des cours pour la démo
873
+ const mockCourses = [
874
+ { id: '1', title: 'Philosophie Morale', author: 'Kant' },
875
+ { id: '2', title: 'Philosophie Politique', author: 'Rousseau' },
876
+ { id: '3', title: 'Métaphysique', author: 'Descartes' },
877
+ { id: '4', title: 'Éthique', author: 'Spinoza' }
878
+ ];
879
+
880
+ const select = $('#course-select');
881
+ mockCourses.forEach(course => {
882
+ const newOption = new Option(course.title, course.id, false, false);
883
+ $(newOption).data('author', course.author);
884
+ select.append(newOption);
885
  });
886
+ select.trigger('change');
887
  }
888
  loadCourses();
889
 
 
957
  });
958
 
959
  $('#dissertations-list').on('click', '.delete-btn', function(e) {
960
+ e.stopPropagation();
961
  const index = $(this).data('index');
962
  deleteDissertation(index);
963
  });
964
 
965
  $('#copy-btn').click(function() {
966
  const htmlToCopy = $('#response > div').html();
 
967
  const textToCopy = new DOMParser().parseFromString(htmlToCopy, 'text/html').body.textContent || "";
968
  navigator.clipboard.writeText(textToCopy).then(() => {
969
  Toast.fire({ icon: 'success', title: 'Copié dans le presse-papiers!' });
 
976
  let ticking = false;
977
 
978
  function updateScrollPosition() {
 
979
  ticking = false;
980
  }
981
 
 
991
  window.addEventListener('resize', function() {
992
  clearTimeout(resizeTimer);
993
  resizeTimer = setTimeout(function() {
 
994
  if (window.innerWidth <= 640) {
 
995
  $('body').addClass('mobile-optimized');
996
  } else {
997
  $('body').removeClass('mobile-optimized');
 
1003
  if ('ontouchstart' in window) {
1004
  $('body').addClass('touch-device');
1005
 
 
1006
  $(document).on('touchstart', 'button, .collapsible', function() {
1007
  $(this).addClass('touch-active');
1008
  });