Spaces:
Running
Running
Update templates/index.html
Browse files- templates/index.html +356 -731
templates/index.html
CHANGED
@@ -7,7 +7,7 @@
|
|
7 |
<title>Mariam AI - Assistant Français Intelligent</title>
|
8 |
<script src="https://cdn.tailwindcss.com"></script>
|
9 |
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/4.0.2/marked.min.js"></script>
|
10 |
-
|
11 |
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
|
12 |
<style>
|
13 |
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;500;600;700&display=swap');
|
@@ -20,7 +20,7 @@
|
|
20 |
--accent: #6366f1;
|
21 |
--success: #38a169;
|
22 |
--danger: #e53e3e;
|
23 |
-
--warning: #f97316;
|
24 |
--background: #f7fafc;
|
25 |
--card-bg: rgba(255, 255, 255, 0.95);
|
26 |
}
|
@@ -29,11 +29,13 @@
|
|
29 |
font-family: 'Plus Jakarta Sans', sans-serif;
|
30 |
background-color: #fafafa;
|
31 |
scroll-behavior: smooth;
|
|
|
32 |
}
|
33 |
|
34 |
.glassmorphism {
|
35 |
background: rgba(255, 255, 255, 0.95);
|
36 |
backdrop-filter: blur(10px);
|
|
|
37 |
border: 1px solid rgba(255, 255, 255, 0.3);
|
38 |
box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.07);
|
39 |
}
|
@@ -50,29 +52,17 @@
|
|
50 |
.gradient-text {
|
51 |
background: linear-gradient(135deg, var(--primary-dark) 0%, var(--primary) 100%);
|
52 |
-webkit-background-clip: text;
|
|
|
53 |
-webkit-text-fill-color: transparent;
|
|
|
54 |
}
|
55 |
|
56 |
.loading-dot {
|
57 |
animation: bounce 1.4s infinite;
|
58 |
}
|
59 |
-
|
60 |
-
.loading-dot:nth-child(
|
61 |
-
|
62 |
-
}
|
63 |
-
|
64 |
-
.loading-dot:nth-child(3) {
|
65 |
-
animation-delay: 0.4s;
|
66 |
-
}
|
67 |
-
|
68 |
-
@keyframes bounce {
|
69 |
-
0%, 80%, 100% {
|
70 |
-
transform: translateY(0);
|
71 |
-
}
|
72 |
-
40% {
|
73 |
-
transform: translateY(-6px);
|
74 |
-
}
|
75 |
-
}
|
76 |
|
77 |
.custom-radio:checked+span {
|
78 |
background: linear-gradient(135deg, var(--primary-dark) 0%, var(--primary) 100%);
|
@@ -80,502 +70,187 @@
|
|
80 |
border-color: var(--primary);
|
81 |
}
|
82 |
|
83 |
-
/* Styles pour les messages d'
|
84 |
-
.alert-message {
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
}
|
93 |
-
.alert-
|
94 |
-
margin-right: 0.75rem; /* space-x-3 equivalent */
|
95 |
-
font-size: 1.25rem; /* text-xl */
|
96 |
-
margin-top: 0.125rem; /* slight top margin for better alignment */
|
97 |
-
}
|
98 |
-
.alert-message div p:first-child {
|
99 |
-
font-weight: 500; /* font-medium */
|
100 |
-
}
|
101 |
-
.alert-message div p:last-child {
|
102 |
-
font-size: 0.875rem; /* text-sm */
|
103 |
-
margin-top: 0.25rem; /* mt-1 */
|
104 |
-
line-height: 1.4; /* Improved line height for wrap */
|
105 |
-
}
|
106 |
-
|
107 |
-
.alert-warning {
|
108 |
-
color: #92400e; /* text-amber-700 */
|
109 |
-
background-color: #fffbeb; /* bg-amber-50 */
|
110 |
-
border-color: #fde68a /* border-amber-200 */
|
111 |
-
}
|
112 |
-
.alert-warning i {
|
113 |
-
color: var(--warning); /* text-amber-500 */
|
114 |
-
}
|
115 |
-
.alert-warning div p:last-child {
|
116 |
-
color: #b45309; /* text-amber-600 */
|
117 |
-
}
|
118 |
-
|
119 |
-
.alert-danger {
|
120 |
-
color: #991b1b; /* text-red-700 */
|
121 |
-
background-color: #fef2f2; /* bg-red-50 */
|
122 |
-
border-color: #fecaca; /* border-red-200 */
|
123 |
-
}
|
124 |
-
.alert-danger i {
|
125 |
-
color: var(--danger); /* text-red-500 */
|
126 |
-
}
|
127 |
-
.alert-danger div p:last-child {
|
128 |
-
color: #b91c1c; /* text-red-600 */
|
129 |
-
}
|
130 |
-
|
131 |
|
132 |
/* Animations */
|
133 |
-
@keyframes fadeIn {
|
134 |
-
|
135 |
-
|
136 |
-
}
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
}
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
}
|
146 |
-
|
147 |
-
.
|
148 |
-
|
149 |
-
}
|
150 |
-
|
151 |
-
.
|
152 |
-
|
153 |
-
|
154 |
-
}
|
155 |
-
|
156 |
-
.
|
157 |
-
|
158 |
-
|
159 |
-
}
|
160 |
-
|
161 |
-
.
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
}
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
}
|
178 |
-
|
179 |
-
|
180 |
-
/*
|
181 |
-
.
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
}
|
187 |
-
|
188 |
-
.
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
|
195 |
-
transition: transform 0.2s ease;
|
196 |
-
}
|
197 |
-
|
198 |
-
.image-preview-item:hover {
|
199 |
-
transform: scale(1.05);
|
200 |
-
}
|
201 |
-
|
202 |
-
.image-preview-item img {
|
203 |
-
width: 100%;
|
204 |
-
height: 100%;
|
205 |
-
object-fit: cover;
|
206 |
-
}
|
207 |
-
|
208 |
-
.remove-image {
|
209 |
-
position: absolute;
|
210 |
-
top: 5px;
|
211 |
-
right: 5px;
|
212 |
-
background-color: rgba(0, 0, 0, 0.6);
|
213 |
-
color: white;
|
214 |
-
border: none;
|
215 |
-
border-radius: 50%;
|
216 |
-
width: 22px;
|
217 |
-
height: 22px;
|
218 |
-
display: flex;
|
219 |
-
align-items: center;
|
220 |
-
justify-content: center;
|
221 |
-
cursor: pointer;
|
222 |
-
font-size: 12px;
|
223 |
-
opacity: 0;
|
224 |
-
transition: opacity 0.2s, transform 0.2s;
|
225 |
-
}
|
226 |
-
|
227 |
-
.image-preview-item:hover .remove-image {
|
228 |
-
opacity: 1;
|
229 |
-
}
|
230 |
-
|
231 |
-
.remove-image:hover {
|
232 |
-
background-color: rgba(0, 0, 0, 0.8);
|
233 |
-
transform: scale(1.1);
|
234 |
-
}
|
235 |
-
|
236 |
-
/* Style pour préserver les espaces dans le markdown */
|
237 |
-
.prose {
|
238 |
-
white-space: pre-wrap;
|
239 |
-
word-wrap: break-word;
|
240 |
-
overflow-wrap: break-word;
|
241 |
-
color: #2d3748;
|
242 |
-
line-height: 1.7;
|
243 |
-
}
|
244 |
-
|
245 |
-
.prose h1, .prose h2, .prose h3, .prose h4 {
|
246 |
-
color: var(--primary-dark);
|
247 |
-
margin-top: 1.5em;
|
248 |
-
margin-bottom: 0.5em;
|
249 |
-
font-weight: 600;
|
250 |
-
}
|
251 |
-
|
252 |
-
.prose p {
|
253 |
-
margin-bottom: 1em;
|
254 |
-
}
|
255 |
-
|
256 |
-
.prose ul, .prose ol {
|
257 |
-
padding-left: 1.5em;
|
258 |
-
margin-bottom: 1em;
|
259 |
-
}
|
260 |
-
|
261 |
-
.prose blockquote {
|
262 |
-
border-left: 4px solid var(--primary);
|
263 |
-
padding-left: 1em;
|
264 |
-
font-style: italic;
|
265 |
-
color: var(--secondary);
|
266 |
-
margin: 1em 0;
|
267 |
-
}
|
268 |
-
|
269 |
-
/* Loading animation */
|
270 |
-
.loader {
|
271 |
-
display: inline-block;
|
272 |
-
position: relative;
|
273 |
-
width: 80px;
|
274 |
-
height: 20px;
|
275 |
-
}
|
276 |
-
|
277 |
-
.loader div {
|
278 |
-
position: absolute;
|
279 |
-
top: 8px;
|
280 |
-
width: 13px;
|
281 |
-
height: 13px;
|
282 |
-
border-radius: 50%;
|
283 |
-
background: var(--primary);
|
284 |
-
animation-timing-function: cubic-bezier(0, 1, 1, 0);
|
285 |
-
}
|
286 |
-
|
287 |
-
.loader div:nth-child(1) {
|
288 |
-
left: 8px;
|
289 |
-
animation: loader1 0.6s infinite;
|
290 |
-
}
|
291 |
-
|
292 |
-
.loader div:nth-child(2) {
|
293 |
-
left: 8px;
|
294 |
-
animation: loader2 0.6s infinite;
|
295 |
-
}
|
296 |
-
|
297 |
-
.loader div:nth-child(3) {
|
298 |
-
left: 32px;
|
299 |
-
animation: loader2 0.6s infinite;
|
300 |
-
}
|
301 |
-
|
302 |
-
.loader div:nth-child(4) {
|
303 |
-
left: 56px;
|
304 |
-
animation: loader3 0.6s infinite;
|
305 |
-
}
|
306 |
-
|
307 |
-
@keyframes loader1 {
|
308 |
-
0% {transform: scale(0);}
|
309 |
-
100% {transform: scale(1);}
|
310 |
-
}
|
311 |
-
|
312 |
-
@keyframes loader2 {
|
313 |
-
0% {transform: translate(0, 0);}
|
314 |
-
100% {transform: translate(24px, 0);}
|
315 |
-
}
|
316 |
-
|
317 |
-
@keyframes loader3 {
|
318 |
-
0% {transform: scale(1);}
|
319 |
-
100% {transform: scale(0);}
|
320 |
-
}
|
321 |
-
|
322 |
-
/* Focus styles */
|
323 |
-
.focus-ring {
|
324 |
-
position: relative;
|
325 |
-
}
|
326 |
-
|
327 |
-
.focus-ring:focus-within::after {
|
328 |
-
content: '';
|
329 |
-
position: absolute;
|
330 |
-
top: -3px;
|
331 |
-
left: -3px;
|
332 |
-
right: -3px;
|
333 |
-
bottom: -3px;
|
334 |
-
border-radius: 14px;
|
335 |
-
border: 2px solid rgba(59, 130, 246, 0.3);
|
336 |
-
pointer-events: none;
|
337 |
-
animation: focusIn 0.2s ease-out forwards;
|
338 |
-
}
|
339 |
-
|
340 |
-
@keyframes focusIn {
|
341 |
-
from { opacity: 0; transform: scale(0.98); }
|
342 |
-
to { opacity: 1; transform: scale(1); }
|
343 |
-
}
|
344 |
-
|
345 |
-
/* Style optionnel pour améliorer la lisibilité sur mobile */
|
346 |
-
@media (max-width: 640px) {
|
347 |
-
.prose {
|
348 |
-
font-size: 0.95rem;
|
349 |
-
line-height: 1.6;
|
350 |
-
}
|
351 |
-
.grid-cols-4 { /* Assure que les boutons radio s'affichent bien */
|
352 |
-
grid-template-columns: repeat(2, minmax(0, 1fr));
|
353 |
-
}
|
354 |
-
}
|
355 |
-
|
356 |
-
/* Style pour la checkbox DeepThink */
|
357 |
-
.deepthink-label {
|
358 |
-
display: flex;
|
359 |
-
align-items: center;
|
360 |
-
cursor: pointer;
|
361 |
-
font-size: 0.875rem; /* text-sm */
|
362 |
-
color: var(--secondary);
|
363 |
-
}
|
364 |
-
.deepthink-label input[type="checkbox"] {
|
365 |
-
appearance: none;
|
366 |
-
width: 1.25em;
|
367 |
-
height: 1.25em;
|
368 |
-
border: 2px solid #cbd5e0; /* border-gray-300 */
|
369 |
-
border-radius: 0.375rem; /* rounded-md */
|
370 |
-
margin-right: 0.5em;
|
371 |
-
position: relative;
|
372 |
-
top: 1px;
|
373 |
-
transition: all 0.2s ease-in-out;
|
374 |
-
cursor: pointer; /* Add cursor pointer */
|
375 |
-
}
|
376 |
-
.deepthink-label input[type="checkbox"]:checked {
|
377 |
-
background-color: var(--primary);
|
378 |
-
border-color: var(--primary);
|
379 |
-
}
|
380 |
-
.deepthink-label input[type="checkbox"]:checked::after {
|
381 |
-
content: '\f00c'; /* FontAwesome check icon */
|
382 |
-
font-family: 'Font Awesome 6 Free';
|
383 |
-
font-weight: 900;
|
384 |
-
color: white;
|
385 |
-
font-size: 0.8em;
|
386 |
-
position: absolute;
|
387 |
-
top: 50%;
|
388 |
-
left: 50%;
|
389 |
-
transform: translate(-50%, -50%);
|
390 |
-
}
|
391 |
-
.deepthink-label input[type="checkbox"]:disabled {
|
392 |
-
background-color: #e2e8f0; /* gray-200 */
|
393 |
-
border-color: #cbd5e0; /* gray-300 */
|
394 |
-
cursor: not-allowed;
|
395 |
-
}
|
396 |
-
.deepthink-label input[type="checkbox"]:disabled + span { /* Style text when disabled */
|
397 |
-
color: #a0aec0; /* gray-400 */
|
398 |
-
}
|
399 |
-
.deepthink-label input[type="checkbox"]:disabled:checked::after {
|
400 |
-
color: #a0aec0; /* gray-400 */
|
401 |
-
}
|
402 |
-
|
403 |
-
.deepthink-label:hover input[type="checkbox"]:not(:disabled) {
|
404 |
-
border-color: var(--primary-dark);
|
405 |
-
}
|
406 |
-
.deepthink-tooltip {
|
407 |
-
position: relative;
|
408 |
-
display: inline-flex; /* Use inline-flex for alignment */
|
409 |
-
align-items: center; /* Align icon vertically */
|
410 |
-
margin-left: 6px;
|
411 |
-
}
|
412 |
-
.deepthink-tooltip .tooltip-text {
|
413 |
-
visibility: hidden;
|
414 |
-
width: 220px;
|
415 |
-
background-color: #2d3748; /* gray-800 */
|
416 |
-
color: #fff;
|
417 |
-
text-align: center;
|
418 |
-
border-radius: 6px;
|
419 |
-
padding: 6px 10px;
|
420 |
-
position: absolute;
|
421 |
-
z-index: 10;
|
422 |
-
bottom: 135%;
|
423 |
-
left: 50%;
|
424 |
-
margin-left: -110px;
|
425 |
-
opacity: 0;
|
426 |
-
transition: opacity 0.3s;
|
427 |
-
font-size: 0.75rem; /* text-xs */
|
428 |
-
line-height: 1.4;
|
429 |
-
pointer-events: none; /* Prevent tooltip from interfering */
|
430 |
-
}
|
431 |
-
.deepthink-tooltip .tooltip-text::after {
|
432 |
-
content: "";
|
433 |
-
position: absolute;
|
434 |
-
top: 100%;
|
435 |
-
left: 50%;
|
436 |
-
margin-left: -5px;
|
437 |
-
border-width: 5px;
|
438 |
-
border-style: solid;
|
439 |
-
border-color: #2d3748 transparent transparent transparent;
|
440 |
-
}
|
441 |
-
.deepthink-tooltip:hover .tooltip-text {
|
442 |
-
visibility: visible;
|
443 |
-
opacity: 1;
|
444 |
-
}
|
445 |
-
/* MODIFICATION: Style pour le statut DeepThink */
|
446 |
-
#deepthink-status {
|
447 |
-
font-style: italic;
|
448 |
-
margin-left: 0.5rem; /* ml-2 */
|
449 |
-
}
|
450 |
|
451 |
</style>
|
452 |
</head>
|
453 |
|
454 |
-
<body class="min-h-screen bg-gradient-to-br from-blue-50 via-white to-blue-50">
|
455 |
<nav class="glassmorphism sticky top-0 z-50 border-b border-blue-100">
|
456 |
-
<div class="container mx-auto px-6 py-4">
|
457 |
<div class="flex items-center justify-between">
|
458 |
-
<div class="flex items-center space-x-4">
|
459 |
<div class="bg-gradient-to-r from-blue-600 to-blue-800 rounded-lg p-2 shadow-lg transform hover:scale-110 transition-transform duration-300">
|
460 |
-
<i class="fas fa-robot text-white text-xl"></i>
|
461 |
</div>
|
462 |
-
<h1 class="text-2xl font-bold gradient-text">Mariam AI</h1>
|
463 |
</div>
|
464 |
-
<div class="text-blue-900 font-medium bg-blue-50 py-1 px-4 rounded-full shadow-sm">Assistant Français
|
|
|
465 |
</div>
|
466 |
</div>
|
467 |
</nav>
|
468 |
|
469 |
-
<main class="container mx-auto px-6 py-12">
|
470 |
-
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
471 |
<!-- Section Travail Argumentatif -->
|
472 |
<div class="card-hover glassmorphism rounded-2xl overflow-hidden scale-in">
|
473 |
-
<div class="p-8">
|
474 |
-
<div class="flex items-center space-x-4 mb-8">
|
475 |
-
<div class="bg-blue-100 rounded-lg p-3 transform rotate-3">
|
476 |
-
<i class="fas fa-pen-fancy text-blue-600 text-xl"></i>
|
477 |
</div>
|
478 |
-
<h2 class="text-2xl font-bold text-gray-800">Travail Argumentatif</h2>
|
479 |
</div>
|
480 |
-
<form id="francais-form" class="space-y-8">
|
481 |
-
<div class="space-y-
|
482 |
<label for="sujet-francais" class="block text-sm font-medium text-gray-700 flex items-center">
|
483 |
<i class="fas fa-book-open mr-2 text-blue-500"></i>Sujet <span class="text-red-500 ml-1">*</span>
|
484 |
</label>
|
485 |
<div class="focus-ring">
|
486 |
<textarea id="sujet-francais" name="sujet" rows="4"
|
487 |
-
class="w-full px-4 py-3 rounded-xl border-2 border-gray-200 focus:border-blue-500 focus:outline-none transition-all duration-200 resize-none"
|
488 |
placeholder="Entrez votre sujet ici..." required></textarea>
|
489 |
</div>
|
490 |
<div class="text-xs text-gray-400 text-right" id="character-count">0 caractères</div>
|
491 |
</div>
|
492 |
|
493 |
-
<div class="space-y-
|
494 |
<label class="block text-sm font-medium text-gray-700 flex items-center">
|
495 |
<i class="fas fa-tasks mr-2 text-blue-500"></i>Type d'argumentation
|
496 |
</label>
|
497 |
-
|
|
|
498 |
<label class="relative">
|
499 |
-
<input type="radio" name="choix" value="Etaye"
|
500 |
-
|
501 |
-
<span
|
502 |
-
class="flex items-center justify-center p-3 border-2 border-gray-200 rounded-xl text-sm font-medium transition-all duration-200 hover:border-blue-400">
|
503 |
-
Étayer
|
504 |
-
</span>
|
505 |
</label>
|
506 |
<label class="relative">
|
507 |
-
<input type="radio" name="choix" value="refute"
|
508 |
-
|
509 |
-
<span
|
510 |
-
class="flex items-center justify-center p-3 border-2 border-gray-200 rounded-xl text-sm font-medium transition-all duration-200 hover:border-blue-400">
|
511 |
-
Réfuter
|
512 |
-
</span>
|
513 |
</label>
|
514 |
<label class="relative">
|
515 |
-
<input type="radio" name="choix" value="discuter"
|
516 |
-
|
517 |
-
<span
|
518 |
-
class="flex items-center justify-center p-3 border-2 border-gray-200 rounded-xl text-sm font-medium transition-all duration-200 hover:border-blue-400">
|
519 |
-
Discuter
|
520 |
-
</span>
|
521 |
</label>
|
522 |
<label class="relative">
|
523 |
-
<input type="radio" name="choix" value="dissertation"
|
524 |
-
|
525 |
-
<span class="flex items-center justify-center p-3 border-2 border-gray-200 rounded-xl text-sm font-medium transition-all duration-200 hover:border-blue-400">
|
526 |
-
Dissertation
|
527 |
-
</span>
|
528 |
</label>
|
529 |
</div>
|
530 |
</div>
|
531 |
|
532 |
-
<div class="space-y-
|
533 |
<label class="block text-sm font-medium text-gray-700 flex items-center">
|
534 |
<i class="fas fa-feather-alt mr-2 text-blue-500"></i>Style d'écriture
|
535 |
</label>
|
536 |
-
<div class="grid grid-cols-2 gap-4">
|
537 |
<label class="relative">
|
538 |
-
<input type="radio" name="style" value="raffiné"
|
539 |
-
|
540 |
-
<span
|
541 |
-
class="flex items-center justify-center p-3 border-2 border-gray-200 rounded-xl text-sm font-medium transition-all duration-200 hover:border-blue-400">
|
542 |
-
Raffiné
|
543 |
-
</span>
|
544 |
</label>
|
545 |
<label class="relative">
|
546 |
-
<input type="radio" name="style" value="Normal"
|
547 |
-
|
548 |
-
<span
|
549 |
-
class="flex items-center justify-center p-3 border-2 border-gray-200 rounded-xl text-sm font-medium transition-all duration-200 hover:border-blue-400">
|
550 |
-
Normal
|
551 |
-
</span>
|
552 |
</label>
|
553 |
</div>
|
554 |
</div>
|
555 |
|
556 |
-
|
557 |
-
<div class="flex items-center justify-start pt-2">
|
558 |
<label for="deepthink-checkbox" class="deepthink-label">
|
559 |
<input type="checkbox" id="deepthink-checkbox" name="use_deepthink_visual">
|
560 |
-
<span>Utiliser DeepThink</span>
|
561 |
<div class="deepthink-tooltip">
|
562 |
-
<i class="fas fa-info-circle text-gray-400"></i>
|
563 |
-
<span class="tooltip-text">
|
564 |
</div>
|
565 |
</label>
|
566 |
-
<span id="deepthink-status" class="text-xs text-gray-500"></span>
|
567 |
</div>
|
568 |
-
<!-- Fin de la modification -->
|
569 |
|
570 |
<button type="submit"
|
571 |
-
class="w-full bg-gradient-to-r from-blue-600 to-blue-800 text-white px-6 py-4 rounded-xl font-medium hover:from-blue-700 hover:to-blue-900 transition-all duration-300 transform hover:scale-[1.02] active:scale-[0.98] shadow-lg">
|
572 |
-
<div class="flex items-center justify-center space-x-3">
|
573 |
<i class="fas fa-magic"></i>
|
574 |
<span>Générer</span>
|
575 |
</div>
|
576 |
</button>
|
577 |
</form>
|
578 |
-
|
|
|
579 |
<!-- Le contenu généré sera inséré ici -->
|
580 |
</div>
|
581 |
</div>
|
@@ -583,50 +258,52 @@
|
|
583 |
|
584 |
<!-- Section Étude de texte -->
|
585 |
<div class="card-hover glassmorphism rounded-2xl overflow-hidden scale-in" style="animation-delay: 0.1s;">
|
586 |
-
|
587 |
-
<div class="flex items-center space-x-4 mb-8">
|
588 |
-
<div class="bg-blue-100 rounded-lg p-3 transform -rotate-3">
|
589 |
-
<i class="fas fa-file-alt text-blue-600 text-xl"></i>
|
590 |
</div>
|
591 |
-
<h2 class="text-2xl font-bold text-gray-800">Étude de texte</h2>
|
592 |
</div>
|
593 |
-
<form id="etude-texte-form" class="space-y-8" enctype="multipart/form-data">
|
594 |
-
<div class="space-y-
|
595 |
<label class="block text-sm font-medium text-gray-700 flex items-center">
|
596 |
<i class="fas fa-image mr-2 text-blue-500"></i>Image(s) du texte <span class="text-red-500 ml-1">*</span>
|
597 |
</label>
|
598 |
-
<div class="border-3 border-dashed border-gray-300 rounded-xl p-8 text-center cursor-pointer hover:border-blue-400 transition-all duration-200 group"
|
599 |
id="drop-zone">
|
600 |
<input type="file" id="image-upload" name="images" accept="image/*" class="hidden" multiple>
|
601 |
-
<div class="space-y-4">
|
602 |
-
<div class="bg-blue-50 rounded-full w-16 h-16 flex items-center justify-center mx-auto transform transition-transform group-hover:scale-110 group-hover:rotate-6">
|
603 |
-
<i class="fas fa-cloud-upload-alt text-3xl text-blue-500"></i>
|
604 |
</div>
|
605 |
-
<p class="text-sm text-gray-600 font-medium">Glissez
|
606 |
-
|
607 |
-
<p class="text-xs text-gray-400">PNG, JPG, WEBP jusqu'à 10MB</p>
|
608 |
</div>
|
609 |
</div>
|
610 |
<div id="image-preview" class="image-preview"></div>
|
611 |
</div>
|
612 |
|
613 |
<button type="submit"
|
614 |
-
class="w-full bg-gradient-to-r from-blue-600 to-blue-800 text-white px-6 py-4 rounded-xl font-medium hover:from-blue-700 hover:to-blue-900 transition-all duration-300 transform hover:scale-[1.02] active:scale-[0.98] shadow-lg">
|
615 |
-
<div class="flex items-center justify-center space-x-3">
|
616 |
<i class="fas fa-search"></i>
|
617 |
<span>Analyser</span>
|
618 |
</div>
|
619 |
</button>
|
620 |
</form>
|
621 |
-
|
|
|
622 |
<!-- Le contenu analysé sera inséré ici -->
|
623 |
</div>
|
624 |
</div>
|
625 |
</div>
|
626 |
</div>
|
627 |
-
|
628 |
-
|
629 |
-
|
|
|
|
|
630 |
Sauvegardes
|
631 |
</h2>
|
632 |
<div id="backups-list" class="space-y-4">
|
@@ -635,23 +312,21 @@
|
|
635 |
</div>
|
636 |
</main>
|
637 |
|
638 |
-
<footer class="bg-gray-50 border-t border-gray-200 py-6 mt-16">
|
639 |
-
<div class="container mx-auto px-6">
|
640 |
-
<div class="flex flex-col md:flex-row justify-between items-center">
|
641 |
-
<div class="flex items-center space-x-2
|
642 |
<div class="bg-blue-600 rounded-lg p-1 shadow">
|
643 |
<i class="fas fa-robot text-white text-sm"></i>
|
644 |
</div>
|
645 |
<span class="text-gray-600 text-sm">Mariam AI</span>
|
646 |
</div>
|
647 |
-
<div class="flex space-x-6">
|
648 |
-
<a href="#" class="text-gray-500 hover:text-blue-600 transition-colors">
|
649 |
-
<i class="fas fa-question-circle"></i>
|
650 |
-
<span class="ml-1 text-sm">Aide</span>
|
651 |
</a>
|
652 |
-
<a href="#" class="text-gray-500 hover:text-blue-600 transition-colors">
|
653 |
-
<i class="fas fa-shield-alt"></i>
|
654 |
-
<span class="ml-1 text-sm">Confidentialité</span>
|
655 |
</a>
|
656 |
</div>
|
657 |
</div>
|
@@ -660,80 +335,50 @@
|
|
660 |
|
661 |
<script>
|
662 |
// --- Constantes ---
|
663 |
-
const DEEPTHINK_STORAGE_KEY = 'deepThinkLastUsedDate';
|
664 |
|
665 |
// --- Gestionnaire de fichiers ---
|
666 |
const uploadedFiles = new Map();
|
667 |
-
// ... (initializeFileUpload, highlightDropZone, unhighlightDropZone, handleFiles, renderPreview, removeFile restent identiques) ...
|
668 |
function initializeFileUpload() {
|
669 |
const dropZone = document.getElementById('drop-zone');
|
670 |
const fileInput = document.getElementById('image-upload');
|
671 |
const imagePreview = document.getElementById('image-preview');
|
|
|
672 |
|
673 |
dropZone.addEventListener('click', () => fileInput.click());
|
|
|
|
|
|
|
|
|
674 |
|
675 |
-
|
676 |
-
|
677 |
-
});
|
678 |
-
|
679 |
-
['dragleave', 'drop'].forEach(eventName => {
|
680 |
-
dropZone.addEventListener(eventName, unhighlightDropZone);
|
681 |
-
});
|
682 |
-
|
683 |
-
dropZone.addEventListener('drop', (e) => {
|
684 |
-
e.preventDefault();
|
685 |
-
const files = e.dataTransfer.files;
|
686 |
-
handleFiles(files);
|
687 |
-
});
|
688 |
-
|
689 |
-
fileInput.addEventListener('change', () => {
|
690 |
-
const files = fileInput.files;
|
691 |
-
handleFiles(files);
|
692 |
-
fileInput.value = '';
|
693 |
-
});
|
694 |
-
|
695 |
-
function highlightDropZone(e) {
|
696 |
-
e.preventDefault();
|
697 |
-
dropZone.classList.add('border-blue-500', 'bg-blue-50');
|
698 |
-
}
|
699 |
-
|
700 |
-
function unhighlightDropZone(e) {
|
701 |
-
e.preventDefault();
|
702 |
-
dropZone.classList.remove('border-blue-500', 'bg-blue-50');
|
703 |
-
}
|
704 |
|
705 |
function handleFiles(files) {
|
706 |
-
const currentFiles = fileInput.files;
|
707 |
const dataTransfer = new DataTransfer();
|
708 |
-
|
709 |
-
|
710 |
-
|
711 |
-
if (!file.type.startsWith('image/')) continue;
|
712 |
const fileId = `${file.name}-${file.size}`;
|
713 |
-
if (uploadedFiles.has(fileId))
|
714 |
uploadedFiles.set(fileId, file);
|
715 |
dataTransfer.items.add(file);
|
716 |
renderPreview(file, fileId);
|
717 |
-
}
|
718 |
-
|
719 |
}
|
720 |
|
721 |
function renderPreview(file, fileId) {
|
722 |
const reader = new FileReader();
|
723 |
-
|
724 |
-
|
725 |
-
|
726 |
-
|
727 |
-
|
728 |
-
|
729 |
-
|
730 |
-
|
731 |
-
|
732 |
-
previewItem.querySelector('.remove-image').addEventListener('click', () => {
|
733 |
-
removeFile(fileId, previewItem);
|
734 |
-
});
|
735 |
-
};
|
736 |
-
reader.readAsDataURL(file);
|
737 |
}
|
738 |
|
739 |
function removeFile(fileId, previewElement) {
|
@@ -743,166 +388,151 @@
|
|
743 |
fileInput.files = dataTransfer.files;
|
744 |
previewElement.style.opacity = '0';
|
745 |
previewElement.style.transform = 'scale(0.9)';
|
746 |
-
setTimeout(() => {
|
747 |
-
if (imagePreview.contains(previewElement)) {
|
748 |
-
imagePreview.removeChild(previewElement);
|
749 |
-
}
|
750 |
-
}, 300);
|
751 |
}
|
752 |
}
|
753 |
|
754 |
-
|
755 |
// --- Gestion des sauvegardes ---
|
756 |
-
|
757 |
-
function sauvegarderReponse(titre, contenu) {
|
758 |
const sauvegardes = JSON.parse(localStorage.getItem('mariam_ai_sauvegardes') || '[]');
|
759 |
const dateSauvegarde = new Date().toISOString();
|
760 |
const MAX_SAUVEGARDES = 20;
|
761 |
-
if (sauvegardes.length >= MAX_SAUVEGARDES)
|
762 |
-
|
763 |
-
}
|
764 |
-
sauvegardes.push({
|
765 |
-
titre: titre || "Sauvegarde sans titre",
|
766 |
-
contenu,
|
767 |
-
date: dateSauvegarde
|
768 |
-
});
|
769 |
try {
|
770 |
localStorage.setItem('mariam_ai_sauvegardes', JSON.stringify(sauvegardes));
|
|
|
|
|
771 |
} catch (e) {
|
772 |
-
console.error("Erreur
|
773 |
-
displayNotification("Erreur
|
774 |
-
return;
|
775 |
}
|
776 |
-
displayNotification("Sauvegardé avec succès", "success");
|
777 |
-
afficherSauvegardes();
|
778 |
}
|
779 |
|
780 |
function displayNotification(message, type = 'success') {
|
781 |
const notification = document.createElement('div');
|
782 |
-
const
|
783 |
-
|
784 |
-
|
785 |
-
|
786 |
-
|
787 |
-
|
788 |
-
|
789 |
-
|
790 |
-
|
791 |
-
|
792 |
-
|
793 |
setTimeout(() => {
|
794 |
-
notification.style.opacity = '0';
|
795 |
-
notification.
|
796 |
-
|
797 |
-
|
798 |
-
|
799 |
-
|
800 |
-
|
801 |
-
|
802 |
-
|
803 |
-
|
804 |
-
|
805 |
-
|
806 |
-
|
807 |
-
|
808 |
-
|
809 |
-
|
|
|
|
|
|
|
|
|
810 |
}
|
811 |
|
812 |
-
|
813 |
function afficherSauvegardes() {
|
814 |
-
const sauvegardes = JSON.parse(localStorage.getItem('mariam_ai_sauvegardes') || '[]');
|
815 |
-
sauvegardes.sort((a, b) => new Date(b.date) - new Date(a.date));
|
816 |
const backupsList = document.getElementById('backups-list');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
817 |
backupsList.innerHTML = '';
|
818 |
|
819 |
if (sauvegardes.length === 0) {
|
820 |
-
backupsList.innerHTML =
|
821 |
-
<div class="bg-blue-50 rounded-lg p-8 text-center">
|
822 |
-
<div class="bg-white rounded-full w-16 h-16 flex items-center justify-center mx-auto mb-4 shadow-sm">
|
823 |
-
<i class="fas fa-folder-open text-blue-300 text-2xl"></i>
|
824 |
-
</div>
|
825 |
-
<p class="text-gray-600">Aucune sauvegarde pour le moment.</p>
|
826 |
-
<p class="text-sm text-gray-500 mt-2">Vos réponses générées apparaîtront ici.</p>
|
827 |
-
</div>
|
828 |
-
`;
|
829 |
return;
|
830 |
}
|
831 |
|
832 |
sauvegardes.forEach((sauvegarde, index) => {
|
833 |
const date = new Date(sauvegarde.date);
|
834 |
const formattedDate = date.toLocaleDateString('fr-FR', { day: 'numeric', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit' });
|
835 |
-
const displayTitle = sauvegarde.titre.length >
|
836 |
|
837 |
const sauvegardeDiv = document.createElement('div');
|
838 |
-
sauvegardeDiv.dataset.
|
839 |
sauvegardeDiv.className = 'bg-white rounded-lg shadow-md p-4 relative backup-item hover:bg-blue-50 transition-all duration-200';
|
840 |
sauvegardeDiv.innerHTML = `
|
841 |
<div class="flex items-start cursor-pointer item-header">
|
842 |
-
<div class="bg-blue-100 rounded-lg p-2 mr-3 flex-shrink-0"> <i class="fas fa-file-alt text-blue-600"></i> </div>
|
843 |
-
<div class="flex-grow overflow-hidden">
|
844 |
-
<h3 class="text-lg font-semibold text-gray-800 truncate" title="${sauvegarde.titre}">${displayTitle}</h3>
|
845 |
-
<p class="text-sm text-gray-600 mb-
|
846 |
</div>
|
847 |
-
<div class="flex space-x-2 ml-
|
848 |
-
<button class="text-gray-400 hover:text-blue-600 focus:outline-none copy-btn p-1" title="Copier"> <i class="fas fa-copy"></i> </button>
|
849 |
-
<button class="text-gray-400 hover:text-red-600 focus:outline-none delete-btn p-1" title="Supprimer"> <i class="fas fa-trash-alt"></i> </button>
|
850 |
</div>
|
851 |
</div>
|
852 |
-
<div class="backup-content
|
853 |
backupsList.appendChild(sauvegardeDiv);
|
854 |
|
855 |
const backupContentDiv = sauvegardeDiv.querySelector('.backup-content');
|
856 |
const headerDiv = sauvegardeDiv.querySelector('.item-header');
|
857 |
|
858 |
-
headerDiv.addEventListener('click', (
|
859 |
const isExpanded = backupContentDiv.classList.contains('backup-content-expanded');
|
860 |
document.querySelectorAll('.backup-content-expanded').forEach(content => {
|
861 |
-
if (content !== backupContentDiv) {
|
862 |
-
content.classList.remove('backup-content-expanded'); content.innerHTML = '';
|
863 |
-
}
|
864 |
});
|
865 |
if (!isExpanded) {
|
866 |
-
backupContentDiv.innerHTML = marked.parse(sauvegarde.contenu);
|
867 |
backupContentDiv.classList.add('backup-content-expanded');
|
868 |
-
|
869 |
} else {
|
870 |
backupContentDiv.classList.remove('backup-content-expanded'); backupContentDiv.innerHTML = '';
|
871 |
}
|
872 |
});
|
873 |
|
874 |
-
|
875 |
-
copyButton.addEventListener('click', (e) => {
|
876 |
e.stopPropagation();
|
877 |
-
navigator.clipboard.writeText(sauvegarde.contenu).then(() => {
|
878 |
-
const icon =
|
879 |
}).catch(err => { console.error('Copy error:', err); displayNotification("Erreur copie", "error"); });
|
880 |
});
|
881 |
|
882 |
-
|
883 |
-
deleteButton.addEventListener('click', (e) => {
|
884 |
e.stopPropagation();
|
885 |
-
const
|
886 |
-
|
887 |
-
|
888 |
-
|
889 |
-
|
890 |
-
|
|
|
|
|
891 |
|
892 |
-
|
|
|
893 |
|
894 |
const confirmationModal = document.createElement('div');
|
895 |
-
confirmationModal.className = 'fixed inset-0 flex items-center justify-center z-[100] bg-black bg-opacity-
|
896 |
confirmationModal.innerHTML = `
|
897 |
-
<div class="bg-white rounded-lg p-6 max-w-sm
|
898 |
-
<div class="flex items-center mb-4"> <div class="bg-red-100 rounded-full p-2 mr-3"><i class="fas fa-exclamation-triangle text-red-500"></i></div> <h3 class="text-lg font-semibold text-gray-800">Confirmation</h3> </div>
|
899 |
<p class="text-gray-600 mb-6 text-sm">Supprimer cette sauvegarde ?</p>
|
900 |
<div class="flex justify-end space-x-3"> <button class="px-4 py-2 border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-50 cancel-btn text-sm">Annuler</button> <button class="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 confirm-btn text-sm">Supprimer</button> </div>
|
901 |
</div>`;
|
902 |
document.body.appendChild(confirmationModal);
|
903 |
confirmationModal.querySelector('.cancel-btn').addEventListener('click', () => closeModal(confirmationModal));
|
904 |
confirmationModal.querySelector('.confirm-btn').addEventListener('click', () => { supprimerSauvegarde(originalIndex); closeModal(confirmationModal); });
|
905 |
-
|
906 |
});
|
907 |
});
|
908 |
}
|
@@ -915,26 +545,23 @@
|
|
915 |
|
916 |
|
917 |
// --- Gestion DeepThink ---
|
918 |
-
/**
|
919 |
-
* Vérifie si DeepThink a déjà été utilisé aujourd'hui et met à jour l'UI.
|
920 |
-
*/
|
921 |
function checkDeepThinkAvailability() {
|
922 |
const checkbox = document.getElementById('deepthink-checkbox');
|
923 |
const statusSpan = document.getElementById('deepthink-status');
|
924 |
-
if (!checkbox || !statusSpan) return;
|
925 |
-
|
926 |
-
|
927 |
-
|
928 |
-
|
929 |
-
|
930 |
-
|
931 |
-
|
932 |
-
|
933 |
-
|
934 |
-
}
|
935 |
-
|
936 |
-
|
937 |
-
statusSpan.
|
938 |
}
|
939 |
}
|
940 |
|
@@ -944,79 +571,73 @@
|
|
944 |
const output = document.getElementById('francais-output');
|
945 |
const sujetTextarea = document.getElementById('sujet-francais');
|
946 |
const deepThinkCheckbox = document.getElementById('deepthink-checkbox');
|
|
|
|
|
|
|
947 |
|
948 |
form.addEventListener('submit', async (e) => {
|
949 |
e.preventDefault();
|
|
|
|
|
|
|
950 |
|
951 |
const sujetValue = sujetTextarea.value.trim();
|
952 |
-
const isDeepThinkChecked = deepThinkCheckbox.checked;
|
953 |
|
954 |
-
//
|
955 |
if (!sujetValue) {
|
956 |
output.innerHTML = `<div class="alert-message alert-warning"><i class="fas fa-exclamation-triangle"></i><div><p>Champ obligatoire</p><p>Veuillez entrer un sujet.</p></div></div>`;
|
957 |
-
sujetTextarea.focus();
|
958 |
-
|
959 |
-
setTimeout(() => sujetTextarea.classList.remove('border-red-500'), 3000);
|
960 |
return;
|
961 |
}
|
962 |
-
|
963 |
-
// MODIFICATION: Vérifier la limite DeepThink avant l'envoi si coché
|
964 |
if (isDeepThinkChecked) {
|
965 |
-
|
966 |
-
|
967 |
-
|
968 |
-
|
969 |
-
|
970 |
-
|
971 |
-
|
|
|
|
|
|
|
972 |
}
|
973 |
-
// --- Fin Validation ---
|
974 |
|
|
|
|
|
975 |
|
976 |
-
// --- Affichage chargement ---
|
977 |
-
output.innerHTML = `
|
978 |
-
<div class="flex flex-col items-center justify-center py-8">
|
979 |
-
<div class="loader mb-4"><div></div><div></div><div></div><div></div></div>
|
980 |
-
<p class="text-sm font-medium text-gray-600">Génération en cours...</p>
|
981 |
-
${isDeepThinkChecked ? '<p class="text-xs text-blue-500 mt-1">Mode DeepThink activé</p>' : ''}
|
982 |
-
</div>`;
|
983 |
|
984 |
-
// --- Préparation et Envoi ---
|
985 |
const formData = new FormData(form);
|
986 |
-
formData.append('use_deepthink', isDeepThinkChecked);
|
987 |
|
988 |
try {
|
989 |
const response = await fetch('/api/francais', { method: 'POST', body: formData });
|
990 |
const result = await response.json();
|
991 |
-
|
992 |
if (!response.ok) throw new Error(result.output || `Erreur HTTP ${response.status}`);
|
993 |
|
994 |
-
//
|
995 |
-
output.
|
996 |
-
output.style.transition = '';
|
997 |
-
output.innerHTML = marked.parse(result.output);
|
998 |
-
requestAnimationFrame(() => {
|
999 |
-
output.style.transition = 'opacity 0.3s ease-in-out';
|
1000 |
-
output.style.opacity = '1';
|
1001 |
-
});
|
1002 |
|
1003 |
-
|
1004 |
-
const titreSauvegarde = sujetValue.length > 100 ? sujetValue.substring(0, 97) + '...' : sujetValue;
|
1005 |
const deepThinkSuffix = isDeepThinkChecked ? ' [DeepThink]' : '';
|
1006 |
sauvegarderReponse(titreSauvegarde + deepThinkSuffix, result.output);
|
1007 |
|
1008 |
-
// MODIFICATION: Si DeepThink a été utilisé avec succès, enregistrer la date et mettre à jour l'UI
|
1009 |
if (isDeepThinkChecked) {
|
1010 |
-
|
1011 |
-
|
1012 |
-
|
|
|
|
|
1013 |
}
|
|
|
1014 |
|
1015 |
-
|
1016 |
-
|
1017 |
-
} catch (error) { // --- Gestion Erreur ---
|
1018 |
console.error("Erreur soumission (français):", error);
|
1019 |
-
output.innerHTML = `<div class="alert-message alert-danger"><i class="fas fa-exclamation-circle"></i><div><p>Erreur</p><p>${error.message || "Vérifiez la console
|
|
|
|
|
|
|
1020 |
}
|
1021 |
});
|
1022 |
}
|
@@ -1025,72 +646,75 @@
|
|
1025 |
const form = document.getElementById('etude-texte-form');
|
1026 |
const output = document.getElementById('etude-texte-output');
|
1027 |
const fileInput = document.getElementById('image-upload');
|
|
|
|
|
|
|
1028 |
|
1029 |
form.addEventListener('submit', async (e) => {
|
1030 |
e.preventDefault();
|
|
|
|
|
|
|
|
|
|
|
1031 |
if (uploadedFiles.size === 0) {
|
1032 |
output.innerHTML = `<div class="alert-message alert-warning"><i class="fas fa-exclamation-triangle"></i><div><p>Aucune image</p><p>Ajoutez au moins une image.</p></div></div>`;
|
|
|
1033 |
return;
|
1034 |
}
|
1035 |
-
|
1036 |
-
|
1037 |
-
|
1038 |
-
|
1039 |
-
<p class="text-xs text-gray-400 mt-2">Cela peut prendre un moment</p>
|
1040 |
-
</div>`;
|
1041 |
const formData = new FormData();
|
1042 |
uploadedFiles.forEach((file) => formData.append('images', file, file.name));
|
1043 |
try {
|
1044 |
const response = await fetch('/api/etude-texte', { method: 'POST', body: formData });
|
1045 |
const result = await response.json();
|
1046 |
if (!response.ok) throw new Error(result.output || `Erreur HTTP ${response.status}`);
|
1047 |
-
|
1048 |
-
output.
|
1049 |
-
|
1050 |
-
requestAnimationFrame(() => { output.style.transition = 'opacity 0.3s ease-in-out'; output.style.opacity = '1'; });
|
1051 |
const titre = `Analyse image(s) - ${new Date().toLocaleDateString('fr-FR')}`;
|
1052 |
sauvegarderReponse(titre, result.output);
|
1053 |
-
|
1054 |
} catch (error) {
|
1055 |
console.error("Erreur soumission (étude texte):", error);
|
1056 |
-
output.innerHTML = `<div class="alert-message alert-danger"><i class="fas fa-exclamation-circle"></i><div><p>Erreur</p><p>${error.message || "Vérifiez la console
|
|
|
|
|
|
|
1057 |
}
|
1058 |
});
|
1059 |
}
|
1060 |
|
1061 |
// --- Animations & Améliorations UI ---
|
1062 |
-
// ... (animateOnScroll, enhanceTextareaFocus, enhanceRadioButtons restent identiques) ...
|
1063 |
function animateOnScroll() {
|
1064 |
const cards = document.querySelectorAll('.card-hover');
|
1065 |
if (!('IntersectionObserver' in window)) { cards.forEach(card => card.style.opacity = '1'); return; }
|
1066 |
const observer = new IntersectionObserver((entries) => {
|
1067 |
entries.forEach((entry) => {
|
1068 |
-
if (entry.isIntersecting) {
|
1069 |
-
requestAnimationFrame(() => { entry.target.style.opacity = '1'; entry.target.style.transform = 'translateY(0)'; });
|
1070 |
-
observer.unobserve(entry.target);
|
1071 |
-
}
|
1072 |
});
|
1073 |
-
}, { threshold: 0.
|
1074 |
-
cards.forEach(card => { card.style.opacity = '0'; card.style.transform = 'translateY(
|
1075 |
}
|
1076 |
function enhanceTextareaFocus() {
|
1077 |
const textarea = document.getElementById('sujet-francais');
|
1078 |
const counter = document.getElementById('character-count');
|
1079 |
-
textarea
|
1080 |
-
|
1081 |
-
});
|
1082 |
}
|
1083 |
function enhanceRadioButtons() {
|
1084 |
-
|
1085 |
-
radioGroups.forEach(label => {
|
1086 |
const input = label.previousElementSibling; if (input.disabled) return;
|
1087 |
-
label.addEventListener('mouseenter', () => { if (!input.checked) label.classList.add('border-blue-400', 'shadow-md', 'transform' ,'-translate-y-
|
1088 |
-
label.addEventListener('mouseleave', () => { if (!input.checked) label.classList.remove('border-blue-400', 'shadow-md', 'transform', '-translate-y-
|
1089 |
input.addEventListener('change', () => {
|
1090 |
const groupName = input.name;
|
1091 |
document.querySelectorAll(`input[name="${groupName}"] + span`).forEach(otherLabel => {
|
1092 |
-
if (otherLabel !== label) otherLabel.classList.remove('border-blue-400', 'shadow-md', 'transform', '-translate-y-
|
1093 |
-
else label.classList.remove('shadow-md', 'transform', '-translate-y-
|
1094 |
});
|
1095 |
});
|
1096 |
});
|
@@ -1105,27 +729,28 @@
|
|
1105 |
animateOnScroll();
|
1106 |
enhanceTextareaFocus();
|
1107 |
enhanceRadioButtons();
|
1108 |
-
checkDeepThinkAvailability();
|
1109 |
afficherSauvegardes();
|
1110 |
-
|
1111 |
-
// MathJax Config
|
1112 |
-
window.MathJax = { tex: { inlineMath: [['$', '$'], ['\\(', '\\)']], displayMath: [['$$', '$$'], ['\\[', '\\]']] }, svg: { fontCache: 'global' }, options: { skipHtmlTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code'], ignoreHtmlClass: 'tex2jax_ignore', processHtmlClass: 'tex2jax_process' } };
|
1113 |
|
1114 |
// Welcome Message (session based)
|
1115 |
-
|
1116 |
-
|
1117 |
-
|
1118 |
-
|
1119 |
-
|
1120 |
-
|
1121 |
-
|
1122 |
-
|
1123 |
-
|
1124 |
-
|
1125 |
-
|
|
|
|
|
|
|
1126 |
function closeWelcomeMessage(element) {
|
1127 |
-
element.classList.add('opacity-0', 'translate-y-4');
|
1128 |
-
setTimeout(() => { if (element
|
1129 |
}
|
1130 |
});
|
1131 |
</script>
|
|
|
7 |
<title>Mariam AI - Assistant Français Intelligent</title>
|
8 |
<script src="https://cdn.tailwindcss.com"></script>
|
9 |
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/4.0.2/marked.min.js"></script>
|
10 |
+
<!-- MathJax retiré -->
|
11 |
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
|
12 |
<style>
|
13 |
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;500;600;700&display=swap');
|
|
|
20 |
--accent: #6366f1;
|
21 |
--success: #38a169;
|
22 |
--danger: #e53e3e;
|
23 |
+
--warning: #f97316;
|
24 |
--background: #f7fafc;
|
25 |
--card-bg: rgba(255, 255, 255, 0.95);
|
26 |
}
|
|
|
29 |
font-family: 'Plus Jakarta Sans', sans-serif;
|
30 |
background-color: #fafafa;
|
31 |
scroll-behavior: smooth;
|
32 |
+
-webkit-tap-highlight-color: transparent; /* Remove blue tap highlight on mobile */
|
33 |
}
|
34 |
|
35 |
.glassmorphism {
|
36 |
background: rgba(255, 255, 255, 0.95);
|
37 |
backdrop-filter: blur(10px);
|
38 |
+
-webkit-backdrop-filter: blur(10px); /* Safari */
|
39 |
border: 1px solid rgba(255, 255, 255, 0.3);
|
40 |
box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.07);
|
41 |
}
|
|
|
52 |
.gradient-text {
|
53 |
background: linear-gradient(135deg, var(--primary-dark) 0%, var(--primary) 100%);
|
54 |
-webkit-background-clip: text;
|
55 |
+
background-clip: text;
|
56 |
-webkit-text-fill-color: transparent;
|
57 |
+
text-fill-color: transparent; /* Standard property */
|
58 |
}
|
59 |
|
60 |
.loading-dot {
|
61 |
animation: bounce 1.4s infinite;
|
62 |
}
|
63 |
+
.loading-dot:nth-child(2) { animation-delay: 0.2s; }
|
64 |
+
.loading-dot:nth-child(3) { animation-delay: 0.4s; }
|
65 |
+
@keyframes bounce { 0%, 80%, 100% { transform: translateY(0); } 40% { transform: translateY(-6px); } }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
66 |
|
67 |
.custom-radio:checked+span {
|
68 |
background: linear-gradient(135deg, var(--primary-dark) 0%, var(--primary) 100%);
|
|
|
70 |
border-color: var(--primary);
|
71 |
}
|
72 |
|
73 |
+
/* Styles pour les messages d'alerte */
|
74 |
+
.alert-message { display: flex; align-items: flex-start; padding: 1rem; border-radius: 0.5rem; border-width: 1px; border-style: solid; }
|
75 |
+
.alert-message i { margin-right: 0.75rem; font-size: 1.25rem; margin-top: 0.125rem; flex-shrink: 0; }
|
76 |
+
.alert-message div p:first-child { font-weight: 500; }
|
77 |
+
.alert-message div p:last-child { font-size: 0.875rem; margin-top: 0.25rem; line-height: 1.4; }
|
78 |
+
.alert-warning { color: #92400e; background-color: #fffbeb; border-color: #fde68a; }
|
79 |
+
.alert-warning i { color: var(--warning); }
|
80 |
+
.alert-warning div p:last-child { color: #b45309; }
|
81 |
+
.alert-danger { color: #991b1b; background-color: #fef2f2; border-color: #fecaca; }
|
82 |
+
.alert-danger i { color: var(--danger); }
|
83 |
+
.alert-danger div p:last-child { color: #b91c1c; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
84 |
|
85 |
/* Animations */
|
86 |
+
@keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
|
87 |
+
.fade-in { animation: fadeIn 0.3s ease-out forwards; }
|
88 |
+
@keyframes scaleIn { from { transform: scale(0.95); opacity: 0; } to { transform: scale(1); opacity: 1; } }
|
89 |
+
.scale-in { animation: scaleIn 0.3s ease-out forwards; }
|
90 |
+
|
91 |
+
/* Sauvegardes */
|
92 |
+
.backup-item { cursor: pointer; transition: all 0.25s ease; }
|
93 |
+
.backup-item:hover { transform: translateY(-2px); box-shadow: 0 6px 12px rgba(0, 0, 0, 0.05); }
|
94 |
+
.backup-content { display: none; margin-top: 10px; max-height: 0; overflow: hidden; transition: max-height 0.3s ease-out, opacity 0.3s ease-out, margin-top 0.3s ease-out, padding-top 0.3s ease-out; opacity: 0; padding-top: 0; }
|
95 |
+
.backup-content-expanded { display: block; max-height: 3000px; opacity: 1; margin-top: 1rem; padding-top: 0.75rem; }
|
96 |
+
|
97 |
+
/* Upload Images */
|
98 |
+
.image-preview { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 15px; }
|
99 |
+
.image-preview-item { position: relative; width: 100px; height: 100px; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); transition: transform 0.2s ease; }
|
100 |
+
.image-preview-item:hover { transform: scale(1.05); }
|
101 |
+
.image-preview-item img { width: 100%; height: 100%; object-fit: cover; }
|
102 |
+
.remove-image { position: absolute; top: 4px; right: 4px; background-color: rgba(0, 0, 0, 0.6); color: white; border: none; border-radius: 50%; width: 20px; height: 20px; display: flex; align-items: center; justify-content: center; cursor: pointer; font-size: 11px; opacity: 0; transition: opacity 0.2s, transform 0.2s; }
|
103 |
+
.image-preview-item:hover .remove-image { opacity: 1; }
|
104 |
+
.remove-image:hover { background-color: rgba(0, 0, 0, 0.8); transform: scale(1.1); }
|
105 |
+
|
106 |
+
/* Style Prose (Zone de sortie) */
|
107 |
+
.prose { white-space: pre-wrap; word-wrap: break-word; overflow-wrap: break-word; color: #2d3748; line-height: 1.7; font-size: 0.95rem; /* légèrement plus petit par défaut */ }
|
108 |
+
@media (min-width: 640px) { .prose { font-size: 1rem; } } /* Taille normale sur sm+ */
|
109 |
+
.prose h1, .prose h2, .prose h3, .prose h4 { color: var(--primary-dark); margin-top: 1.5em; margin-bottom: 0.5em; font-weight: 600; }
|
110 |
+
.prose p { margin-bottom: 1em; }
|
111 |
+
.prose ul, .prose ol { padding-left: 1.5em; margin-bottom: 1em; }
|
112 |
+
.prose blockquote { border-left: 4px solid var(--primary); padding-left: 1em; font-style: italic; color: var(--secondary); margin: 1em 0; }
|
113 |
+
.prose code { background-color: #edf2f7; /* gray-200 */ padding: 0.2em 0.4em; border-radius: 0.25rem; font-size: 0.9em; }
|
114 |
+
.prose pre { background-color: #1a202c; /* gray-900 */ color: #e2e8f0; /* gray-200 */ padding: 1em; border-radius: 0.375rem; overflow-x: auto; }
|
115 |
+
.prose pre code { background-color: transparent; padding: 0; border-radius: 0; font-size: 0.875em; }
|
116 |
+
|
117 |
+
/* Loader */
|
118 |
+
.loader { display: inline-block; position: relative; width: 64px; height: 16px; /* Smaller */ }
|
119 |
+
.loader div { position: absolute; top: 6px; width: 10px; height: 10px; border-radius: 50%; background: var(--primary); animation-timing-function: cubic-bezier(0, 1, 1, 0); }
|
120 |
+
.loader div:nth-child(1) { left: 6px; animation: loader1 0.6s infinite; }
|
121 |
+
.loader div:nth-child(2) { left: 6px; animation: loader2 0.6s infinite; }
|
122 |
+
.loader div:nth-child(3) { left: 26px; animation: loader2 0.6s infinite; }
|
123 |
+
.loader div:nth-child(4) { left: 45px; animation: loader3 0.6s infinite; }
|
124 |
+
@keyframes loader1 { 0% { transform: scale(0); } 100% { transform: scale(1); } }
|
125 |
+
@keyframes loader2 { 0% { transform: translate(0, 0); } 100% { transform: translate(20px, 0); } }
|
126 |
+
@keyframes loader3 { 0% { transform: scale(1); } 100% { transform: scale(0); } }
|
127 |
+
|
128 |
+
/* Focus Ring */
|
129 |
+
.focus-ring { position: relative; }
|
130 |
+
.focus-ring:focus-within::after { content: ''; position: absolute; top: -3px; left: -3px; right: -3px; bottom: -3px; border-radius: 14px; border: 2px solid rgba(59, 130, 246, 0.3); pointer-events: none; animation: focusIn 0.2s ease-out forwards; }
|
131 |
+
@keyframes focusIn { from { opacity: 0; transform: scale(0.98); } to { opacity: 1; transform: scale(1); } }
|
132 |
+
|
133 |
+
/* DeepThink Checkbox */
|
134 |
+
.deepthink-label { display: flex; align-items: center; cursor: pointer; font-size: 0.875rem; color: var(--secondary); }
|
135 |
+
.deepthink-label input[type="checkbox"] { appearance: none; width: 1.25em; height: 1.25em; border: 2px solid #cbd5e0; border-radius: 0.375rem; margin-right: 0.5em; position: relative; top: 1px; transition: all 0.2s ease-in-out; cursor: pointer; flex-shrink: 0; }
|
136 |
+
.deepthink-label input[type="checkbox"]:checked { background-color: var(--primary); border-color: var(--primary); }
|
137 |
+
.deepthink-label input[type="checkbox"]:checked::after { content: '\f00c'; font-family: 'Font Awesome 6 Free'; font-weight: 900; color: white; font-size: 0.8em; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); }
|
138 |
+
.deepthink-label input[type="checkbox"]:disabled { background-color: #e2e8f0; border-color: #cbd5e0; cursor: not-allowed; }
|
139 |
+
.deepthink-label input[type="checkbox"]:disabled + span { color: #a0aec0; }
|
140 |
+
.deepthink-label input[type="checkbox"]:disabled:checked::after { color: #a0aec0; }
|
141 |
+
.deepthink-label:hover input[type="checkbox"]:not(:disabled) { border-color: var(--primary-dark); }
|
142 |
+
.deepthink-tooltip { position: relative; display: inline-flex; align-items: center; margin-left: 6px; }
|
143 |
+
.deepthink-tooltip .tooltip-text { visibility: hidden; width: 200px; background-color: #2d3748; color: #fff; text-align: center; border-radius: 6px; padding: 6px 10px; position: absolute; z-index: 10; bottom: 135%; left: 50%; margin-left: -100px; opacity: 0; transition: opacity 0.3s; font-size: 0.75rem; line-height: 1.4; pointer-events: none; }
|
144 |
+
.deepthink-tooltip .tooltip-text::after { content: ""; position: absolute; top: 100%; left: 50%; margin-left: -5px; border-width: 5px; border-style: solid; border-color: #2d3748 transparent transparent transparent; }
|
145 |
+
.deepthink-tooltip:hover .tooltip-text { visibility: visible; opacity: 1; }
|
146 |
+
#deepthink-status { font-style: italic; margin-left: 0.5rem; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
147 |
|
148 |
</style>
|
149 |
</head>
|
150 |
|
151 |
+
<body class="min-h-screen bg-gradient-to-br from-blue-50 via-white to-blue-50 text-gray-800">
|
152 |
<nav class="glassmorphism sticky top-0 z-50 border-b border-blue-100">
|
153 |
+
<div class="container mx-auto px-4 sm:px-6 py-3 sm:py-4"> {/* Ajustement padding nav */}
|
154 |
<div class="flex items-center justify-between">
|
155 |
+
<div class="flex items-center space-x-3 sm:space-x-4">
|
156 |
<div class="bg-gradient-to-r from-blue-600 to-blue-800 rounded-lg p-2 shadow-lg transform hover:scale-110 transition-transform duration-300">
|
157 |
+
<i class="fas fa-robot text-white text-lg sm:text-xl"></i> {/* Taille icône */}
|
158 |
</div>
|
159 |
+
<h1 class="text-xl sm:text-2xl font-bold gradient-text">Mariam AI</h1> {/* Taille titre */}
|
160 |
</div>
|
161 |
+
<div class="text-xs sm:text-sm text-blue-900 font-medium bg-blue-50 py-1 px-3 sm:px-4 rounded-full shadow-sm hidden sm:block">Assistant Français</div> {/* Masqué sur xs */}
|
162 |
+
<div class="text-xs text-blue-900 font-medium bg-blue-50 py-1 px-3 rounded-full shadow-sm sm:hidden">Assistant</div> {/* Version courte pour xs */}
|
163 |
</div>
|
164 |
</div>
|
165 |
</nav>
|
166 |
|
167 |
+
<main class="container mx-auto px-4 sm:px-6 py-8 sm:py-12"> {/* Ajustement padding main */}
|
168 |
+
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 sm:gap-8">
|
169 |
<!-- Section Travail Argumentatif -->
|
170 |
<div class="card-hover glassmorphism rounded-2xl overflow-hidden scale-in">
|
171 |
+
<div class="p-6 sm:p-8"> {/* Ajustement padding carte */}
|
172 |
+
<div class="flex items-center space-x-3 sm:space-x-4 mb-6 sm:mb-8">
|
173 |
+
<div class="bg-blue-100 rounded-lg p-2 sm:p-3 transform rotate-3">
|
174 |
+
<i class="fas fa-pen-fancy text-blue-600 text-lg sm:text-xl"></i>
|
175 |
</div>
|
176 |
+
<h2 class="text-xl sm:text-2xl font-bold text-gray-800">Travail Argumentatif</h2>
|
177 |
</div>
|
178 |
+
<form id="francais-form" class="space-y-6 sm:space-y-8"> {/* Ajustement espacement */}
|
179 |
+
<div class="space-y-2">
|
180 |
<label for="sujet-francais" class="block text-sm font-medium text-gray-700 flex items-center">
|
181 |
<i class="fas fa-book-open mr-2 text-blue-500"></i>Sujet <span class="text-red-500 ml-1">*</span>
|
182 |
</label>
|
183 |
<div class="focus-ring">
|
184 |
<textarea id="sujet-francais" name="sujet" rows="4"
|
185 |
+
class="w-full px-3 py-2 sm:px-4 sm:py-3 rounded-xl border-2 border-gray-200 focus:border-blue-500 focus:outline-none transition-all duration-200 resize-none text-base"
|
186 |
placeholder="Entrez votre sujet ici..." required></textarea>
|
187 |
</div>
|
188 |
<div class="text-xs text-gray-400 text-right" id="character-count">0 caractères</div>
|
189 |
</div>
|
190 |
|
191 |
+
<div class="space-y-3">
|
192 |
<label class="block text-sm font-medium text-gray-700 flex items-center">
|
193 |
<i class="fas fa-tasks mr-2 text-blue-500"></i>Type d'argumentation
|
194 |
</label>
|
195 |
+
{/* Grid s'adapte déjà bien (2 cols par défaut, 4 sur sm+) */}
|
196 |
+
<div class="grid grid-cols-2 sm:grid-cols-4 gap-3 sm:gap-4">
|
197 |
<label class="relative">
|
198 |
+
<input type="radio" name="choix" value="Etaye" class="custom-radio absolute opacity-0 w-full h-full cursor-pointer" checked>
|
199 |
+
<span class="flex items-center justify-center p-3 border-2 border-gray-200 rounded-xl text-sm font-medium transition-all duration-200 hover:border-blue-400">Étayer</span>
|
|
|
|
|
|
|
|
|
200 |
</label>
|
201 |
<label class="relative">
|
202 |
+
<input type="radio" name="choix" value="refute" class="custom-radio absolute opacity-0 w-full h-full cursor-pointer">
|
203 |
+
<span class="flex items-center justify-center p-3 border-2 border-gray-200 rounded-xl text-sm font-medium transition-all duration-200 hover:border-blue-400">Réfuter</span>
|
|
|
|
|
|
|
|
|
204 |
</label>
|
205 |
<label class="relative">
|
206 |
+
<input type="radio" name="choix" value="discuter" class="custom-radio absolute opacity-0 w-full h-full cursor-pointer">
|
207 |
+
<span class="flex items-center justify-center p-3 border-2 border-gray-200 rounded-xl text-sm font-medium transition-all duration-200 hover:border-blue-400">Discuter</span>
|
|
|
|
|
|
|
|
|
208 |
</label>
|
209 |
<label class="relative">
|
210 |
+
<input type="radio" name="choix" value="dissertation" class="custom-radio absolute opacity-0 w-full h-full cursor-pointer">
|
211 |
+
<span class="flex items-center justify-center p-3 border-2 border-gray-200 rounded-xl text-sm font-medium transition-all duration-200 hover:border-blue-400">Dissertation</span>
|
|
|
|
|
|
|
212 |
</label>
|
213 |
</div>
|
214 |
</div>
|
215 |
|
216 |
+
<div class="space-y-3">
|
217 |
<label class="block text-sm font-medium text-gray-700 flex items-center">
|
218 |
<i class="fas fa-feather-alt mr-2 text-blue-500"></i>Style d'écriture
|
219 |
</label>
|
220 |
+
<div class="grid grid-cols-2 gap-3 sm:gap-4">
|
221 |
<label class="relative">
|
222 |
+
<input type="radio" name="style" value="raffiné" class="custom-radio absolute opacity-0 w-full h-full cursor-pointer" checked>
|
223 |
+
<span class="flex items-center justify-center p-3 border-2 border-gray-200 rounded-xl text-sm font-medium transition-all duration-200 hover:border-blue-400">Raffiné</span>
|
|
|
|
|
|
|
|
|
224 |
</label>
|
225 |
<label class="relative">
|
226 |
+
<input type="radio" name="style" value="Normal" class="custom-radio absolute opacity-0 w-full h-full cursor-pointer">
|
227 |
+
<span class="flex items-center justify-center p-3 border-2 border-gray-200 rounded-xl text-sm font-medium transition-all duration-200 hover:border-blue-400">Normal</span>
|
|
|
|
|
|
|
|
|
228 |
</label>
|
229 |
</div>
|
230 |
</div>
|
231 |
|
232 |
+
<div class="flex items-center justify-start pt-1 sm:pt-2">
|
|
|
233 |
<label for="deepthink-checkbox" class="deepthink-label">
|
234 |
<input type="checkbox" id="deepthink-checkbox" name="use_deepthink_visual">
|
235 |
+
<span class="text-sm">Utiliser DeepThink</span>
|
236 |
<div class="deepthink-tooltip">
|
237 |
+
<i class="fas fa-info-circle text-gray-400 ml-1"></i>
|
238 |
+
<span class="tooltip-text">Modèle avancé (Gemini Pro). Plus lent, meilleure qualité. Limité: 1/jour.</span>
|
239 |
</div>
|
240 |
</label>
|
241 |
+
<span id="deepthink-status" class="text-xs text-gray-500 ml-2"></span>
|
242 |
</div>
|
|
|
243 |
|
244 |
<button type="submit"
|
245 |
+
class="w-full bg-gradient-to-r from-blue-600 to-blue-800 text-white px-5 py-3 sm:px-6 sm:py-4 rounded-xl font-medium hover:from-blue-700 hover:to-blue-900 transition-all duration-300 transform hover:scale-[1.02] active:scale-[0.98] shadow-lg text-base">
|
246 |
+
<div class="flex items-center justify-center space-x-2 sm:space-x-3">
|
247 |
<i class="fas fa-magic"></i>
|
248 |
<span>Générer</span>
|
249 |
</div>
|
250 |
</button>
|
251 |
</form>
|
252 |
+
{/* Ajustement padding et min-height */}
|
253 |
+
<div id="francais-output" class="mt-6 sm:mt-8 p-4 sm:p-6 bg-blue-50 rounded-xl prose max-w-none shadow-inner min-h-[150px]">
|
254 |
<!-- Le contenu généré sera inséré ici -->
|
255 |
</div>
|
256 |
</div>
|
|
|
258 |
|
259 |
<!-- Section Étude de texte -->
|
260 |
<div class="card-hover glassmorphism rounded-2xl overflow-hidden scale-in" style="animation-delay: 0.1s;">
|
261 |
+
<div class="p-6 sm:p-8"> {/* Ajustement padding carte */}
|
262 |
+
<div class="flex items-center space-x-3 sm:space-x-4 mb-6 sm:mb-8">
|
263 |
+
<div class="bg-blue-100 rounded-lg p-2 sm:p-3 transform -rotate-3">
|
264 |
+
<i class="fas fa-file-alt text-blue-600 text-lg sm:text-xl"></i>
|
265 |
</div>
|
266 |
+
<h2 class="text-xl sm:text-2xl font-bold text-gray-800">Étude de texte</h2>
|
267 |
</div>
|
268 |
+
<form id="etude-texte-form" class="space-y-6 sm:space-y-8" enctype="multipart/form-data">
|
269 |
+
<div class="space-y-3">
|
270 |
<label class="block text-sm font-medium text-gray-700 flex items-center">
|
271 |
<i class="fas fa-image mr-2 text-blue-500"></i>Image(s) du texte <span class="text-red-500 ml-1">*</span>
|
272 |
</label>
|
273 |
+
<div class="border-3 border-dashed border-gray-300 rounded-xl p-6 sm:p-8 text-center cursor-pointer hover:border-blue-400 transition-all duration-200 group"
|
274 |
id="drop-zone">
|
275 |
<input type="file" id="image-upload" name="images" accept="image/*" class="hidden" multiple>
|
276 |
+
<div class="space-y-3 sm:space-y-4">
|
277 |
+
<div class="bg-blue-50 rounded-full w-14 h-14 sm:w-16 sm:h-16 flex items-center justify-center mx-auto transform transition-transform group-hover:scale-110 group-hover:rotate-6">
|
278 |
+
<i class="fas fa-cloud-upload-alt text-2xl sm:text-3xl text-blue-500"></i>
|
279 |
</div>
|
280 |
+
<p class="text-sm text-gray-600 font-medium">Glissez/cliquez pour ajouter</p>
|
281 |
+
<p class="text-xs text-gray-400">PNG, JPG, WEBP (max 10MB)</p>
|
|
|
282 |
</div>
|
283 |
</div>
|
284 |
<div id="image-preview" class="image-preview"></div>
|
285 |
</div>
|
286 |
|
287 |
<button type="submit"
|
288 |
+
class="w-full bg-gradient-to-r from-blue-600 to-blue-800 text-white px-5 py-3 sm:px-6 sm:py-4 rounded-xl font-medium hover:from-blue-700 hover:to-blue-900 transition-all duration-300 transform hover:scale-[1.02] active:scale-[0.98] shadow-lg text-base">
|
289 |
+
<div class="flex items-center justify-center space-x-2 sm:space-x-3">
|
290 |
<i class="fas fa-search"></i>
|
291 |
<span>Analyser</span>
|
292 |
</div>
|
293 |
</button>
|
294 |
</form>
|
295 |
+
{/* Ajustement padding et min-height */}
|
296 |
+
<div id="etude-texte-output" class="mt-6 sm:mt-8 p-4 sm:p-6 bg-blue-50 rounded-xl prose max-w-none shadow-inner min-h-[150px]">
|
297 |
<!-- Le contenu analysé sera inséré ici -->
|
298 |
</div>
|
299 |
</div>
|
300 |
</div>
|
301 |
</div>
|
302 |
+
|
303 |
+
<!-- Section Sauvegardes -->
|
304 |
+
<div class="mt-10 sm:mt-12">
|
305 |
+
<h2 class="text-xl sm:text-2xl font-bold text-gray-800 mb-4 sm:mb-6 flex items-center">
|
306 |
+
<i class="fas fa-save mr-2 sm:mr-3 text-blue-500"></i>
|
307 |
Sauvegardes
|
308 |
</h2>
|
309 |
<div id="backups-list" class="space-y-4">
|
|
|
312 |
</div>
|
313 |
</main>
|
314 |
|
315 |
+
<footer class="bg-gray-50 border-t border-gray-200 py-5 sm:py-6 mt-12 sm:mt-16">
|
316 |
+
<div class="container mx-auto px-4 sm:px-6">
|
317 |
+
<div class="flex flex-col md:flex-row justify-between items-center space-y-3 md:space-y-0">
|
318 |
+
<div class="flex items-center space-x-2">
|
319 |
<div class="bg-blue-600 rounded-lg p-1 shadow">
|
320 |
<i class="fas fa-robot text-white text-sm"></i>
|
321 |
</div>
|
322 |
<span class="text-gray-600 text-sm">Mariam AI</span>
|
323 |
</div>
|
324 |
+
<div class="flex space-x-4 sm:space-x-6">
|
325 |
+
<a href="#" class="text-gray-500 hover:text-blue-600 transition-colors text-xs sm:text-sm flex items-center">
|
326 |
+
<i class="fas fa-question-circle mr-1"></i> Aide
|
|
|
327 |
</a>
|
328 |
+
<a href="#" class="text-gray-500 hover:text-blue-600 transition-colors text-xs sm:text-sm flex items-center">
|
329 |
+
<i class="fas fa-shield-alt mr-1"></i> Confidentialité
|
|
|
330 |
</a>
|
331 |
</div>
|
332 |
</div>
|
|
|
335 |
|
336 |
<script>
|
337 |
// --- Constantes ---
|
338 |
+
const DEEPTHINK_STORAGE_KEY = 'deepThinkLastUsedDate';
|
339 |
|
340 |
// --- Gestionnaire de fichiers ---
|
341 |
const uploadedFiles = new Map();
|
|
|
342 |
function initializeFileUpload() {
|
343 |
const dropZone = document.getElementById('drop-zone');
|
344 |
const fileInput = document.getElementById('image-upload');
|
345 |
const imagePreview = document.getElementById('image-preview');
|
346 |
+
if(!dropZone || !fileInput || !imagePreview) return; // Safety check
|
347 |
|
348 |
dropZone.addEventListener('click', () => fileInput.click());
|
349 |
+
['dragenter', 'dragover'].forEach(ev => dropZone.addEventListener(ev, highlightDropZone));
|
350 |
+
['dragleave', 'drop'].forEach(ev => dropZone.addEventListener(ev, unhighlightDropZone));
|
351 |
+
dropZone.addEventListener('drop', (e) => { e.preventDefault(); handleFiles(e.dataTransfer.files); });
|
352 |
+
fileInput.addEventListener('change', () => { handleFiles(fileInput.files); fileInput.value = ''; });
|
353 |
|
354 |
+
function highlightDropZone(e) { e.preventDefault(); dropZone.classList.add('border-blue-500', 'bg-blue-50'); }
|
355 |
+
function unhighlightDropZone(e) { e.preventDefault(); dropZone.classList.remove('border-blue-500', 'bg-blue-50'); }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
356 |
|
357 |
function handleFiles(files) {
|
|
|
358 |
const dataTransfer = new DataTransfer();
|
359 |
+
uploadedFiles.forEach(file => dataTransfer.items.add(file));
|
360 |
+
Array.from(files).forEach(file => {
|
361 |
+
if (!file.type.startsWith('image/')) return;
|
|
|
362 |
const fileId = `${file.name}-${file.size}`;
|
363 |
+
if (uploadedFiles.has(fileId)) return;
|
364 |
uploadedFiles.set(fileId, file);
|
365 |
dataTransfer.items.add(file);
|
366 |
renderPreview(file, fileId);
|
367 |
+
});
|
368 |
+
fileInput.files = dataTransfer.files;
|
369 |
}
|
370 |
|
371 |
function renderPreview(file, fileId) {
|
372 |
const reader = new FileReader();
|
373 |
+
reader.onload = (e) => {
|
374 |
+
const previewItem = document.createElement('div');
|
375 |
+
previewItem.classList.add('image-preview-item', 'scale-in');
|
376 |
+
previewItem.dataset.fileId = fileId;
|
377 |
+
previewItem.innerHTML = `<img src="${e.target.result}" alt="${file.name}"><button class="remove-image" title="Supprimer"><i class="fas fa-times"></i></button>`;
|
378 |
+
imagePreview.appendChild(previewItem);
|
379 |
+
previewItem.querySelector('.remove-image').addEventListener('click', (e) => { e.stopPropagation(); removeFile(fileId, previewItem); });
|
380 |
+
};
|
381 |
+
reader.readAsDataURL(file);
|
|
|
|
|
|
|
|
|
|
|
382 |
}
|
383 |
|
384 |
function removeFile(fileId, previewElement) {
|
|
|
388 |
fileInput.files = dataTransfer.files;
|
389 |
previewElement.style.opacity = '0';
|
390 |
previewElement.style.transform = 'scale(0.9)';
|
391 |
+
setTimeout(() => { if (imagePreview.contains(previewElement)) imagePreview.removeChild(previewElement); }, 300);
|
|
|
|
|
|
|
|
|
392 |
}
|
393 |
}
|
394 |
|
|
|
395 |
// --- Gestion des sauvegardes ---
|
396 |
+
function sauvegarderReponse(titre, contenu) {
|
|
|
397 |
const sauvegardes = JSON.parse(localStorage.getItem('mariam_ai_sauvegardes') || '[]');
|
398 |
const dateSauvegarde = new Date().toISOString();
|
399 |
const MAX_SAUVEGARDES = 20;
|
400 |
+
if (sauvegardes.length >= MAX_SAUVEGARDES) sauvegardes.shift();
|
401 |
+
sauvegardes.push({ titre: titre || "Sauvegarde", contenu, date: dateSauvegarde });
|
|
|
|
|
|
|
|
|
|
|
|
|
402 |
try {
|
403 |
localStorage.setItem('mariam_ai_sauvegardes', JSON.stringify(sauvegardes));
|
404 |
+
displayNotification("Sauvegardé", "success"); // Shorter message
|
405 |
+
afficherSauvegardes();
|
406 |
} catch (e) {
|
407 |
+
console.error("Erreur sauvegarde localStorage:", e);
|
408 |
+
displayNotification("Erreur sauvegarde: Stockage plein?", "error");
|
|
|
409 |
}
|
|
|
|
|
410 |
}
|
411 |
|
412 |
function displayNotification(message, type = 'success') {
|
413 |
const notification = document.createElement('div');
|
414 |
+
const colors = {
|
415 |
+
success: 'bg-green-50 border-green-200 text-green-700',
|
416 |
+
error: 'bg-red-50 border-red-200 text-red-700',
|
417 |
+
warning: 'bg-yellow-50 border-yellow-200 text-yellow-700'
|
418 |
+
};
|
419 |
+
const icons = { success: 'fa-check-circle text-green-500', error: 'fa-exclamation-circle text-red-500', warning: 'fa-exclamation-triangle text-yellow-500' };
|
420 |
+
notification.className = `fixed bottom-4 right-4 sm:bottom-6 sm:right-6 border ${colors[type] || colors.success} px-4 py-3 rounded-lg shadow-lg z-[100] flex items-center scale-in max-w-[calc(100%-2rem)] sm:max-w-sm`; // Responsive max-width
|
421 |
+
notification.innerHTML = `<i class="fas ${icons[type] || icons.success} mr-3 text-lg flex-shrink-0"></i><span class="text-sm font-medium">${message}</span>`;
|
422 |
+
document.body.appendChild(notification);
|
423 |
+
const duration = type === 'error' ? 6000 : 3000;
|
|
|
424 |
setTimeout(() => {
|
425 |
+
notification.style.opacity = '0'; notification.style.transform = 'scale(0.9)'; notification.style.transition = 'opacity 0.3s ease, transform 0.3s ease';
|
426 |
+
setTimeout(() => { if (document.body.contains(notification)) document.body.removeChild(notification); }, 300);
|
427 |
+
}, duration);
|
428 |
+
}
|
429 |
+
|
430 |
+
function supprimerSauvegarde(originalIndex) {
|
431 |
+
try {
|
432 |
+
let sauvegardes = JSON.parse(localStorage.getItem('mariam_ai_sauvegardes') || '[]');
|
433 |
+
if (originalIndex >= 0 && originalIndex < sauvegardes.length) {
|
434 |
+
sauvegardes.splice(originalIndex, 1);
|
435 |
+
localStorage.setItem('mariam_ai_sauvegardes', JSON.stringify(sauvegardes));
|
436 |
+
afficherSauvegardes();
|
437 |
+
} else {
|
438 |
+
console.error("Index de suppression invalide:", originalIndex);
|
439 |
+
displayNotification("Erreur suppression: Index invalide", "error");
|
440 |
+
}
|
441 |
+
} catch (e) {
|
442 |
+
console.error("Erreur suppression sauvegarde:", e);
|
443 |
+
displayNotification("Erreur suppression", "error");
|
444 |
+
}
|
445 |
}
|
446 |
|
|
|
447 |
function afficherSauvegardes() {
|
|
|
|
|
448 |
const backupsList = document.getElementById('backups-list');
|
449 |
+
if(!backupsList) return;
|
450 |
+
let sauvegardes = [];
|
451 |
+
try {
|
452 |
+
sauvegardes = JSON.parse(localStorage.getItem('mariam_ai_sauvegardes') || '[]');
|
453 |
+
} catch(e) { console.error("Erreur lecture sauvegardes:", e); }
|
454 |
+
|
455 |
+
sauvegardes.sort((a, b) => new Date(b.date) - new Date(a.date));
|
456 |
backupsList.innerHTML = '';
|
457 |
|
458 |
if (sauvegardes.length === 0) {
|
459 |
+
backupsList.innerHTML = `<div class="bg-blue-50 rounded-lg p-6 sm:p-8 text-center"><div class="bg-white rounded-full w-16 h-16 flex items-center justify-center mx-auto mb-4 shadow-sm"><i class="fas fa-folder-open text-blue-300 text-2xl"></i></div><p class="text-gray-600 text-sm sm:text-base">Aucune sauvegarde.</p><p class="text-xs sm:text-sm text-gray-500 mt-2">Vos réponses apparaîtront ici.</p></div>`;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
460 |
return;
|
461 |
}
|
462 |
|
463 |
sauvegardes.forEach((sauvegarde, index) => {
|
464 |
const date = new Date(sauvegarde.date);
|
465 |
const formattedDate = date.toLocaleDateString('fr-FR', { day: 'numeric', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit' });
|
466 |
+
const displayTitle = (sauvegarde.titre || "Sans titre").length > 50 ? sauvegarde.titre.substring(0, 47) + '...' : (sauvegarde.titre || "Sans titre");
|
467 |
|
468 |
const sauvegardeDiv = document.createElement('div');
|
469 |
+
sauvegardeDiv.dataset.originalDate = sauvegarde.date; // Use date for more reliable deletion lookup
|
470 |
sauvegardeDiv.className = 'bg-white rounded-lg shadow-md p-4 relative backup-item hover:bg-blue-50 transition-all duration-200';
|
471 |
sauvegardeDiv.innerHTML = `
|
472 |
<div class="flex items-start cursor-pointer item-header">
|
473 |
+
<div class="bg-blue-100 rounded-lg p-2 mr-3 flex-shrink-0"> <i class="fas fa-file-alt text-blue-600 text-sm"></i> </div>
|
474 |
+
<div class="flex-grow overflow-hidden mr-2">
|
475 |
+
<h3 class="text-base sm:text-lg font-semibold text-gray-800 truncate" title="${sauvegarde.titre || 'Sans titre'}">${displayTitle}</h3>
|
476 |
+
<p class="text-xs sm:text-sm text-gray-600 mb-1 flex items-center"> <i class="fas fa-clock text-xs mr-1 text-gray-400"></i> ${formattedDate} </p>
|
477 |
</div>
|
478 |
+
<div class="flex space-x-1 sm:space-x-2 ml-auto flex-shrink-0">
|
479 |
+
<button class="text-gray-400 hover:text-blue-600 focus:outline-none copy-btn p-1 rounded-full hover:bg-gray-100" title="Copier"> <i class="fas fa-copy text-xs sm:text-sm"></i> </button>
|
480 |
+
<button class="text-gray-400 hover:text-red-600 focus:outline-none delete-btn p-1 rounded-full hover:bg-gray-100" title="Supprimer"> <i class="fas fa-trash-alt text-xs sm:text-sm"></i> </button>
|
481 |
</div>
|
482 |
</div>
|
483 |
+
<div class="backup-content text-sm text-gray-700 prose max-w-none border-t border-gray-100"></div>`;
|
484 |
backupsList.appendChild(sauvegardeDiv);
|
485 |
|
486 |
const backupContentDiv = sauvegardeDiv.querySelector('.backup-content');
|
487 |
const headerDiv = sauvegardeDiv.querySelector('.item-header');
|
488 |
|
489 |
+
headerDiv.addEventListener('click', () => {
|
490 |
const isExpanded = backupContentDiv.classList.contains('backup-content-expanded');
|
491 |
document.querySelectorAll('.backup-content-expanded').forEach(content => {
|
492 |
+
if (content !== backupContentDiv) { content.classList.remove('backup-content-expanded'); content.innerHTML = ''; }
|
|
|
|
|
493 |
});
|
494 |
if (!isExpanded) {
|
495 |
+
try { backupContentDiv.innerHTML = marked.parse(sauvegarde.contenu || ''); } catch(e) { backupContentDiv.innerHTML = "Erreur d'affichage du contenu."; console.error(e); }
|
496 |
backupContentDiv.classList.add('backup-content-expanded');
|
497 |
+
// MathJax retiré
|
498 |
} else {
|
499 |
backupContentDiv.classList.remove('backup-content-expanded'); backupContentDiv.innerHTML = '';
|
500 |
}
|
501 |
});
|
502 |
|
503 |
+
sauvegardeDiv.querySelector('.copy-btn').addEventListener('click', (e) => {
|
|
|
504 |
e.stopPropagation();
|
505 |
+
navigator.clipboard.writeText(sauvegarde.contenu || '').then(() => {
|
506 |
+
const icon = e.currentTarget.querySelector('i'); icon.className = 'fas fa-check text-green-500 text-xs sm:text-sm'; setTimeout(() => { icon.className = 'fas fa-copy text-xs sm:text-sm'; }, 2000);
|
507 |
}).catch(err => { console.error('Copy error:', err); displayNotification("Erreur copie", "error"); });
|
508 |
});
|
509 |
|
510 |
+
sauvegardeDiv.querySelector('.delete-btn').addEventListener('click', (e) => {
|
|
|
511 |
e.stopPropagation();
|
512 |
+
const itemDate = sauvegardeDiv.dataset.originalDate;
|
513 |
+
|
514 |
+
// Trouver l'index original basé sur la date (plus fiable que l'index d'affichage trié)
|
515 |
+
let originalIndex = -1;
|
516 |
+
try {
|
517 |
+
const originalSauvegardes = JSON.parse(localStorage.getItem('mariam_ai_sauvegardes') || '[]');
|
518 |
+
originalIndex = originalSauvegardes.findIndex(s => s.date === itemDate);
|
519 |
+
} catch (err) { console.error("Erreur recherche sauvegarde:", err); }
|
520 |
|
521 |
+
|
522 |
+
if (originalIndex === -1) { console.error("Item not found for deletion by date:", itemDate); displayNotification("Erreur suppression", "error"); return; }
|
523 |
|
524 |
const confirmationModal = document.createElement('div');
|
525 |
+
confirmationModal.className = 'fixed inset-0 flex items-center justify-center z-[100] bg-black bg-opacity-50 scale-in p-4'; // Ajout p-4 pour mobile
|
526 |
confirmationModal.innerHTML = `
|
527 |
+
<div class="bg-white rounded-lg p-5 sm:p-6 max-w-sm w-full shadow-xl">
|
528 |
+
<div class="flex items-center mb-4"> <div class="bg-red-100 rounded-full p-2 mr-3"><i class="fas fa-exclamation-triangle text-red-500"></i></div> <h3 class="text-base sm:text-lg font-semibold text-gray-800">Confirmation</h3> </div>
|
529 |
<p class="text-gray-600 mb-6 text-sm">Supprimer cette sauvegarde ?</p>
|
530 |
<div class="flex justify-end space-x-3"> <button class="px-4 py-2 border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-50 cancel-btn text-sm">Annuler</button> <button class="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 confirm-btn text-sm">Supprimer</button> </div>
|
531 |
</div>`;
|
532 |
document.body.appendChild(confirmationModal);
|
533 |
confirmationModal.querySelector('.cancel-btn').addEventListener('click', () => closeModal(confirmationModal));
|
534 |
confirmationModal.querySelector('.confirm-btn').addEventListener('click', () => { supprimerSauvegarde(originalIndex); closeModal(confirmationModal); });
|
535 |
+
confirmationModal.addEventListener('click', (event) => { if (event.target === confirmationModal) closeModal(confirmationModal); });
|
536 |
});
|
537 |
});
|
538 |
}
|
|
|
545 |
|
546 |
|
547 |
// --- Gestion DeepThink ---
|
|
|
|
|
|
|
548 |
function checkDeepThinkAvailability() {
|
549 |
const checkbox = document.getElementById('deepthink-checkbox');
|
550 |
const statusSpan = document.getElementById('deepthink-status');
|
551 |
+
if (!checkbox || !statusSpan) return;
|
552 |
+
try {
|
553 |
+
const lastUsedDateStr = localStorage.getItem(DEEPTHINK_STORAGE_KEY);
|
554 |
+
const todayStr = new Date().toISOString().split('T')[0];
|
555 |
+
if (lastUsedDateStr === todayStr) {
|
556 |
+
checkbox.checked = false; checkbox.disabled = true;
|
557 |
+
statusSpan.textContent = "(utilisé)"; statusSpan.classList.add('text-red-500');
|
558 |
+
} else {
|
559 |
+
checkbox.disabled = false; statusSpan.textContent = ""; statusSpan.classList.remove('text-red-500');
|
560 |
+
}
|
561 |
+
} catch (e) {
|
562 |
+
console.error("Erreur accès localStorage (DeepThink):", e);
|
563 |
+
checkbox.disabled = true; // Disable if error
|
564 |
+
statusSpan.textContent = "(erreur)";
|
565 |
}
|
566 |
}
|
567 |
|
|
|
571 |
const output = document.getElementById('francais-output');
|
572 |
const sujetTextarea = document.getElementById('sujet-francais');
|
573 |
const deepThinkCheckbox = document.getElementById('deepthink-checkbox');
|
574 |
+
const submitButton = form.querySelector('button[type="submit"]'); // Get button for disabling
|
575 |
+
|
576 |
+
if(!form || !output || !sujetTextarea || !deepThinkCheckbox || !submitButton) return; // Safety check
|
577 |
|
578 |
form.addEventListener('submit', async (e) => {
|
579 |
e.preventDefault();
|
580 |
+
const originalButtonContent = submitButton.innerHTML; // Store original content
|
581 |
+
submitButton.disabled = true; // Disable button
|
582 |
+
submitButton.innerHTML = `<div class="flex items-center justify-center"><div class="loader"><div></div><div></div><div></div><div></div></div><span class="ml-2">Génération...</span></div>`; // Show loader in button
|
583 |
|
584 |
const sujetValue = sujetTextarea.value.trim();
|
585 |
+
const isDeepThinkChecked = deepThinkCheckbox.checked;
|
586 |
|
587 |
+
// Validation
|
588 |
if (!sujetValue) {
|
589 |
output.innerHTML = `<div class="alert-message alert-warning"><i class="fas fa-exclamation-triangle"></i><div><p>Champ obligatoire</p><p>Veuillez entrer un sujet.</p></div></div>`;
|
590 |
+
sujetTextarea.focus(); sujetTextarea.classList.add('border-red-500'); setTimeout(() => sujetTextarea.classList.remove('border-red-500'), 3000);
|
591 |
+
submitButton.disabled = false; submitButton.innerHTML = originalButtonContent; // Re-enable button
|
|
|
592 |
return;
|
593 |
}
|
|
|
|
|
594 |
if (isDeepThinkChecked) {
|
595 |
+
try {
|
596 |
+
const lastUsedDateStr = localStorage.getItem(DEEPTHINK_STORAGE_KEY);
|
597 |
+
const todayStr = new Date().toISOString().split('T')[0];
|
598 |
+
if (lastUsedDateStr === todayStr) {
|
599 |
+
output.innerHTML = `<div class="alert-message alert-warning"><i class="fas fa-exclamation-triangle"></i><div><p>Limite atteinte</p><p>DeepThink déjà utilisé aujourd'hui.</p></div></div>`;
|
600 |
+
checkDeepThinkAvailability();
|
601 |
+
submitButton.disabled = false; submitButton.innerHTML = originalButtonContent; // Re-enable button
|
602 |
+
return;
|
603 |
+
}
|
604 |
+
} catch(e) { /* Ignore localStorage error here, proceed without check */ }
|
605 |
}
|
|
|
606 |
|
607 |
+
// Clear previous output and show minimal loader
|
608 |
+
output.innerHTML = `<div class="flex justify-center items-center p-6"><div class="loader"><div></div><div></div><div></div><div></div></div></div>`;
|
609 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
610 |
|
|
|
611 |
const formData = new FormData(form);
|
612 |
+
formData.append('use_deepthink', isDeepThinkChecked);
|
613 |
|
614 |
try {
|
615 |
const response = await fetch('/api/francais', { method: 'POST', body: formData });
|
616 |
const result = await response.json();
|
|
|
617 |
if (!response.ok) throw new Error(result.output || `Erreur HTTP ${response.status}`);
|
618 |
|
619 |
+
// Render output
|
620 |
+
try { output.innerHTML = marked.parse(result.output || ''); } catch (e) { output.innerHTML = "Erreur d'affichage de la réponse."; console.error(e); }
|
|
|
|
|
|
|
|
|
|
|
|
|
621 |
|
622 |
+
const titreSauvegarde = sujetValue.substring(0, 50) + (sujetValue.length > 50 ? '...' : ''); // Shorter title
|
|
|
623 |
const deepThinkSuffix = isDeepThinkChecked ? ' [DeepThink]' : '';
|
624 |
sauvegarderReponse(titreSauvegarde + deepThinkSuffix, result.output);
|
625 |
|
|
|
626 |
if (isDeepThinkChecked) {
|
627 |
+
try {
|
628 |
+
const todayStr = new Date().toISOString().split('T')[0];
|
629 |
+
localStorage.setItem(DEEPTHINK_STORAGE_KEY, todayStr);
|
630 |
+
checkDeepThinkAvailability();
|
631 |
+
} catch(e) { console.error("Erreur sauvegarde date DeepThink:", e); }
|
632 |
}
|
633 |
+
// MathJax retiré
|
634 |
|
635 |
+
} catch (error) {
|
|
|
|
|
636 |
console.error("Erreur soumission (français):", error);
|
637 |
+
output.innerHTML = `<div class="alert-message alert-danger"><i class="fas fa-exclamation-circle"></i><div><p>Erreur</p><p>${error.message || "Vérifiez la console."}</p></div></div>`;
|
638 |
+
} finally {
|
639 |
+
submitButton.disabled = false; // Always re-enable button
|
640 |
+
submitButton.innerHTML = originalButtonContent; // Restore original content
|
641 |
}
|
642 |
});
|
643 |
}
|
|
|
646 |
const form = document.getElementById('etude-texte-form');
|
647 |
const output = document.getElementById('etude-texte-output');
|
648 |
const fileInput = document.getElementById('image-upload');
|
649 |
+
const submitButton = form.querySelector('button[type="submit"]'); // Get button for disabling
|
650 |
+
|
651 |
+
if(!form || !output || !fileInput || !submitButton) return; // Safety check
|
652 |
|
653 |
form.addEventListener('submit', async (e) => {
|
654 |
e.preventDefault();
|
655 |
+
const originalButtonContent = submitButton.innerHTML; // Store original content
|
656 |
+
submitButton.disabled = true; // Disable button
|
657 |
+
submitButton.innerHTML = `<div class="flex items-center justify-center"><div class="loader"><div></div><div></div><div></div><div></div></div><span class="ml-2">Analyse...</span></div>`; // Show loader in button
|
658 |
+
|
659 |
+
|
660 |
if (uploadedFiles.size === 0) {
|
661 |
output.innerHTML = `<div class="alert-message alert-warning"><i class="fas fa-exclamation-triangle"></i><div><p>Aucune image</p><p>Ajoutez au moins une image.</p></div></div>`;
|
662 |
+
submitButton.disabled = false; submitButton.innerHTML = originalButtonContent; // Re-enable button
|
663 |
return;
|
664 |
}
|
665 |
+
|
666 |
+
// Clear previous output and show minimal loader
|
667 |
+
output.innerHTML = `<div class="flex justify-center items-center p-6"><div class="loader"><div></div><div></div><div></div><div></div></div></div>`;
|
668 |
+
|
|
|
|
|
669 |
const formData = new FormData();
|
670 |
uploadedFiles.forEach((file) => formData.append('images', file, file.name));
|
671 |
try {
|
672 |
const response = await fetch('/api/etude-texte', { method: 'POST', body: formData });
|
673 |
const result = await response.json();
|
674 |
if (!response.ok) throw new Error(result.output || `Erreur HTTP ${response.status}`);
|
675 |
+
|
676 |
+
try { output.innerHTML = marked.parse(result.output || ''); } catch (e) { output.innerHTML = "Erreur d'affichage de la réponse."; console.error(e); }
|
677 |
+
|
|
|
678 |
const titre = `Analyse image(s) - ${new Date().toLocaleDateString('fr-FR')}`;
|
679 |
sauvegarderReponse(titre, result.output);
|
680 |
+
// MathJax retiré
|
681 |
} catch (error) {
|
682 |
console.error("Erreur soumission (étude texte):", error);
|
683 |
+
output.innerHTML = `<div class="alert-message alert-danger"><i class="fas fa-exclamation-circle"></i><div><p>Erreur</p><p>${error.message || "Vérifiez la console."}</p></div></div>`;
|
684 |
+
} finally {
|
685 |
+
submitButton.disabled = false; // Always re-enable button
|
686 |
+
submitButton.innerHTML = originalButtonContent; // Restore original content
|
687 |
}
|
688 |
});
|
689 |
}
|
690 |
|
691 |
// --- Animations & Améliorations UI ---
|
|
|
692 |
function animateOnScroll() {
|
693 |
const cards = document.querySelectorAll('.card-hover');
|
694 |
if (!('IntersectionObserver' in window)) { cards.forEach(card => card.style.opacity = '1'); return; }
|
695 |
const observer = new IntersectionObserver((entries) => {
|
696 |
entries.forEach((entry) => {
|
697 |
+
if (entry.isIntersecting) { requestAnimationFrame(() => { entry.target.style.opacity = '1'; entry.target.style.transform = 'translateY(0)'; }); observer.unobserve(entry.target); }
|
|
|
|
|
|
|
698 |
});
|
699 |
+
}, { threshold: 0.05 }); // Lower threshold for earlier trigger
|
700 |
+
cards.forEach(card => { card.style.opacity = '0'; card.style.transform = 'translateY(20px)'; observer.observe(card); }); // Smaller initial translate
|
701 |
}
|
702 |
function enhanceTextareaFocus() {
|
703 |
const textarea = document.getElementById('sujet-francais');
|
704 |
const counter = document.getElementById('character-count');
|
705 |
+
if(!textarea || !counter) return;
|
706 |
+
textarea.addEventListener('input', () => { const length = textarea.value.length; counter.textContent = `${length} caractère${length !== 1 ? 's' : ''}`; textarea.classList.remove('border-red-500'); });
|
|
|
707 |
}
|
708 |
function enhanceRadioButtons() {
|
709 |
+
document.querySelectorAll('input[type="radio"] + span').forEach(label => {
|
|
|
710 |
const input = label.previousElementSibling; if (input.disabled) return;
|
711 |
+
label.addEventListener('mouseenter', () => { if (!input.checked) label.classList.add('border-blue-400', 'shadow-md', 'transform' ,'-translate-y-px'); }); // Smaller translate
|
712 |
+
label.addEventListener('mouseleave', () => { if (!input.checked) label.classList.remove('border-blue-400', 'shadow-md', 'transform', '-translate-y-px'); });
|
713 |
input.addEventListener('change', () => {
|
714 |
const groupName = input.name;
|
715 |
document.querySelectorAll(`input[name="${groupName}"] + span`).forEach(otherLabel => {
|
716 |
+
if (otherLabel !== label) otherLabel.classList.remove('border-blue-400', 'shadow-md', 'transform', '-translate-y-px', 'border-primary');
|
717 |
+
else label.classList.remove('shadow-md', 'transform', '-translate-y-px');
|
718 |
});
|
719 |
});
|
720 |
});
|
|
|
729 |
animateOnScroll();
|
730 |
enhanceTextareaFocus();
|
731 |
enhanceRadioButtons();
|
732 |
+
checkDeepThinkAvailability();
|
733 |
afficherSauvegardes();
|
734 |
+
// MathJax retiré
|
|
|
|
|
735 |
|
736 |
// Welcome Message (session based)
|
737 |
+
try {
|
738 |
+
const showWelcome = sessionStorage.getItem('welcomeShown') !== 'true';
|
739 |
+
if (showWelcome) {
|
740 |
+
const welcomeMessageContainer = document.createElement('div');
|
741 |
+
welcomeMessageContainer.innerHTML = `<div class="fixed bottom-4 right-4 sm:bottom-6 sm:right-6 bg-white rounded-xl shadow-lg p-4 sm:p-5 transform transition-all duration-500 opacity-0 translate-y-4 max-w-[calc(100%-2rem)] sm:max-w-xs z-[100]"><div class="flex items-start space-x-3 sm:space-x-4"><div class="bg-gradient-to-r from-blue-500 to-blue-700 rounded-full p-2 sm:p-3 mt-1 flex-shrink-0"> <i class="fas fa-robot text-white text-lg sm:text-xl"></i> </div><div> <p class="text-sm font-semibold text-gray-800">Bienvenue sur Mariam AI !</p> <p class="text-xs text-gray-500 mt-1">Assistant prêt.</p> </div></div><button class="absolute top-1 right-1 text-gray-400 hover:text-gray-600 focus:outline-none close-welcome p-1"> <i class="fas fa-times text-xs"></i> </button></div>`;
|
742 |
+
document.body.appendChild(welcomeMessageContainer);
|
743 |
+
const welcomeMessage = welcomeMessageContainer.firstElementChild;
|
744 |
+
setTimeout(() => { welcomeMessage.classList.remove('opacity-0', 'translate-y-4'); }, 500);
|
745 |
+
welcomeMessage.querySelector('.close-welcome').addEventListener('click', () => closeWelcomeMessage(welcomeMessage));
|
746 |
+
setTimeout(() => { if (document.body.contains(welcomeMessage)) closeWelcomeMessage(welcomeMessage); }, 7000);
|
747 |
+
sessionStorage.setItem('welcomeShown', 'true');
|
748 |
+
}
|
749 |
+
} catch(e) { console.warn("Erreur accès sessionStorage (Welcome Msg):", e); }
|
750 |
+
|
751 |
function closeWelcomeMessage(element) {
|
752 |
+
element.classList.add('opacity-0', 'translate-y-4'); element.style.transition = 'opacity 0.3s ease, transform 0.3s ease';
|
753 |
+
setTimeout(() => { if (element?.parentElement) element.parentElement.remove(); }, 300);
|
754 |
}
|
755 |
});
|
756 |
</script>
|