samlax12 commited on
Commit
707fdf3
·
verified ·
1 Parent(s): 2d52ac2

Create student.html

Browse files
Files changed (1) hide show
  1. templates/student.html +1382 -0
templates/student.html ADDED
@@ -0,0 +1,1382 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>AI学习助手</title>
7
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css">
8
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.css">
9
+ <!-- KaTeX 用于渲染数学公式 -->
10
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.css">
11
+ <!-- Highlight.js 样式 -->
12
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/github.min.css">
13
+ <style>
14
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
15
+
16
+ :root {
17
+ /* 主要配色 */
18
+ --primary-color: #0f2d49;
19
+ --primary-light: #234a70;
20
+ --secondary-color: #4a6cfd;
21
+ --secondary-light: #7b91ff;
22
+ --tertiary-color: #f7f9fe;
23
+
24
+ /* 功能颜色 */
25
+ --success-color: #10b981;
26
+ --success-light: #d1fae5;
27
+ --warning-color: #f59e0b;
28
+ --warning-light: #fef3c7;
29
+ --danger-color: #ef4444;
30
+ --danger-light: #fee2e2;
31
+ --info-color: #3b82f6;
32
+ --info-light: #dbeafe;
33
+
34
+ /* 中性色 */
35
+ --neutral-50: #f9fafb;
36
+ --neutral-100: #f3f4f6;
37
+ --neutral-200: #e5e7eb;
38
+ --neutral-300: #d1d5db;
39
+ --neutral-400: #9ca3af;
40
+ --neutral-500: #6b7280;
41
+ --neutral-600: #4b5563;
42
+ --neutral-700: #374151;
43
+ --neutral-800: #1f2937;
44
+ --neutral-900: #111827;
45
+
46
+ /* 界面元素 */
47
+ --border-radius-sm: 0.25rem;
48
+ --border-radius: 0.375rem;
49
+ --border-radius-lg: 0.5rem;
50
+ --border-radius-xl: 0.75rem;
51
+ --border-radius-xxl: 1rem;
52
+ --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
53
+ --shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.06);
54
+ --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
55
+ --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
56
+ --shadow-inner: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);
57
+ --transition-base: all 0.2s ease-in-out;
58
+ --transition-smooth: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
59
+ --font-family: 'Inter', 'PingFang SC', 'Microsoft YaHei', sans-serif;
60
+ }
61
+
62
+ body {
63
+ font-family: var(--font-family);
64
+ margin: 0;
65
+ padding: 0;
66
+ height: 100vh;
67
+ background-color: var(--neutral-50);
68
+ color: var(--neutral-800);
69
+ display: flex;
70
+ flex-direction: column;
71
+ -webkit-font-smoothing: antialiased;
72
+ -moz-osx-font-smoothing: grayscale;
73
+ }
74
+
75
+ /* 头部样式 */
76
+ .header {
77
+ background: linear-gradient(135deg, var(--primary-color), var(--primary-light));
78
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
79
+ color: white;
80
+ padding: 1rem 1.5rem;
81
+ box-shadow: var(--shadow-md);
82
+ position: relative;
83
+ z-index: 10;
84
+ }
85
+
86
+ .header-content {
87
+ max-width: 1600px;
88
+ margin: 0 auto;
89
+ display: flex;
90
+ justify-content: space-between;
91
+ align-items: center;
92
+ }
93
+
94
+ .header h1 {
95
+ margin: 0;
96
+ font-size: 1.5rem;
97
+ font-weight: 600;
98
+ color: white;
99
+ display: flex;
100
+ align-items: center;
101
+ }
102
+
103
+ .header h1 i {
104
+ margin-right: 0.75rem;
105
+ color: rgba(255, 255, 255, 0.8);
106
+ }
107
+
108
+ .header .badge {
109
+ background-color: rgba(255, 255, 255, 0.15);
110
+ color: white;
111
+ font-weight: 500;
112
+ padding: 0.35em 0.75em;
113
+ font-size: 0.85rem;
114
+ border-radius: 9999px;
115
+ }
116
+
117
+ /* 主容器样式 */
118
+ .main-container {
119
+ flex: 1;
120
+ display: flex;
121
+ max-width: 1600px;
122
+ margin: 0 auto;
123
+ padding: 1.5rem;
124
+ width: 100%;
125
+ box-sizing: border-box;
126
+ height: calc(100vh - 70px); /* Header height + padding */
127
+ overflow: hidden;
128
+ }
129
+
130
+ .chat-container {
131
+ flex: 1;
132
+ display: flex;
133
+ flex-direction: column;
134
+ background-color: white;
135
+ border-radius: var(--border-radius-xl);
136
+ box-shadow: var(--shadow);
137
+ overflow: hidden;
138
+ height: 100%;
139
+ max-width: 800px;
140
+ margin: 0 auto;
141
+ transition: var(--transition-smooth);
142
+ position: relative;
143
+ }
144
+
145
+ /* 聊天对话区域样式 */
146
+ .chat-messages {
147
+ flex: 1;
148
+ overflow-y: auto;
149
+ padding: 1.5rem;
150
+ display: flex;
151
+ flex-direction: column;
152
+ background-color: var(--neutral-50);
153
+ gap: 1rem;
154
+ scroll-behavior: smooth;
155
+ }
156
+
157
+ /* 自定义滚动条 */
158
+ .chat-messages::-webkit-scrollbar {
159
+ width: 6px;
160
+ height: 6px;
161
+ }
162
+
163
+ .chat-messages::-webkit-scrollbar-track {
164
+ background: var(--neutral-100);
165
+ border-radius: 10px;
166
+ }
167
+
168
+ .chat-messages::-webkit-scrollbar-thumb {
169
+ background: var(--neutral-300);
170
+ border-radius: 10px;
171
+ }
172
+
173
+ .chat-messages::-webkit-scrollbar-thumb:hover {
174
+ background: var(--neutral-400);
175
+ }
176
+
177
+ /* 消息气泡样式 */
178
+ .message {
179
+ max-width: 85%;
180
+ border-radius: var(--border-radius-lg);
181
+ padding: 1rem;
182
+ position: relative;
183
+ line-height: 1.5;
184
+ box-shadow: var(--shadow-sm);
185
+ overflow-wrap: break-word;
186
+ word-wrap: break-word;
187
+ hyphens: auto;
188
+ }
189
+
190
+ .message .message-content {
191
+ padding: 0 10px;
192
+ margin: 0;
193
+ }
194
+
195
+ .user-message {
196
+ background: linear-gradient(135deg, #e9f5ff, #c2e4ff);
197
+ align-self: flex-end;
198
+ color: var(--primary-color);
199
+ border-bottom-right-radius: 0.2rem;
200
+ border-left: 1px solid rgba(74, 108, 253, 0.1);
201
+ border-top: 1px solid rgba(255, 255, 255, 0.5);
202
+ }
203
+
204
+ .bot-message {
205
+ background-color: white;
206
+ align-self: flex-start;
207
+ color: var(--neutral-800);
208
+ border-bottom-left-radius: 0.2rem;
209
+ border-left: 3px solid var(--secondary-color);
210
+ box-shadow: var(--shadow);
211
+ }
212
+
213
+ /* 输入区域样式 */
214
+ .input-container {
215
+ padding: 1rem 1.5rem;
216
+ border-top: 1px solid var(--neutral-200);
217
+ background-color: white;
218
+ position: relative;
219
+ }
220
+
221
+ .input-row {
222
+ display: flex;
223
+ gap: 0.75rem;
224
+ position: relative;
225
+ }
226
+
227
+ .input-field {
228
+ flex: 1;
229
+ padding: 0.85rem 1rem;
230
+ border: 1px solid var(--neutral-200);
231
+ border-radius: var(--border-radius-lg);
232
+ resize: none;
233
+ font-size: 0.95rem;
234
+ box-shadow: var(--shadow-inner);
235
+ transition: var(--transition-base);
236
+ }
237
+
238
+ .input-field:focus {
239
+ outline: none;
240
+ border-color: var(--secondary-color);
241
+ box-shadow: 0 0 0 3px rgba(74, 108, 253, 0.15);
242
+ }
243
+
244
+ .send-button {
245
+ padding: 0 1.25rem;
246
+ background: linear-gradient(135deg, var(--secondary-color), var(--secondary-light));
247
+ color: white;
248
+ border: none;
249
+ border-radius: var(--border-radius-lg);
250
+ cursor: pointer;
251
+ font-weight: 500;
252
+ transition: var(--transition-base);
253
+ display: flex;
254
+ align-items: center;
255
+ justify-content: center;
256
+ }
257
+
258
+ .send-button:hover {
259
+ box-shadow: 0 4px 10px rgba(74, 108, 253, 0.25);
260
+ transform: translateY(-1px);
261
+ }
262
+
263
+ .send-button:disabled {
264
+ background: var(--neutral-300);
265
+ cursor: not-allowed;
266
+ transform: none;
267
+ box-shadow: none;
268
+ }
269
+
270
+ /* 插件容器样式 */
271
+ .plugin-container {
272
+ display: none;
273
+ flex-direction: column;
274
+ background-color: white;
275
+ border-radius: var(--border-radius-xl);
276
+ box-shadow: var(--shadow);
277
+ overflow: hidden;
278
+ height: 100%;
279
+ flex: 2;
280
+ margin-left: 1.5rem;
281
+ transition: var(--transition-smooth);
282
+ }
283
+
284
+ .plugin-header {
285
+ padding: 0.85rem 1.25rem;
286
+ border-bottom: 1px solid var(--neutral-200);
287
+ background-color: var(--neutral-50);
288
+ display: flex;
289
+ justify-content: space-between;
290
+ align-items: center;
291
+ }
292
+
293
+ .plugin-title {
294
+ margin: 0;
295
+ font-size: 1rem;
296
+ font-weight: 600;
297
+ color: var(--primary-color);
298
+ display: flex;
299
+ align-items: center;
300
+ }
301
+
302
+ .plugin-title i {
303
+ margin-right: 0.5rem;
304
+ color: var(--secondary-color);
305
+ }
306
+
307
+ .plugin-close {
308
+ width: 32px;
309
+ height: 32px;
310
+ background: var(--neutral-100);
311
+ border: none;
312
+ border-radius: 50%;
313
+ cursor: pointer;
314
+ font-size: 0.85rem;
315
+ color: var(--neutral-700);
316
+ display: flex;
317
+ align-items: center;
318
+ justify-content: center;
319
+ transition: var(--transition-base);
320
+ }
321
+
322
+ .plugin-close:hover {
323
+ background-color: var(--neutral-200);
324
+ color: var(--neutral-900);
325
+ }
326
+
327
+ .plugin-content {
328
+ flex: 1;
329
+ overflow-y: auto;
330
+ padding: 0;
331
+ background-color: var(--neutral-50);
332
+ }
333
+
334
+ /* 当插件激活时的样式 */
335
+ .main-container.with-plugin .chat-container {
336
+ max-width: 380px;
337
+ margin: 0;
338
+ }
339
+
340
+ /* 代码块样式 */
341
+ pre {
342
+ margin: 1rem 0;
343
+ padding: 1rem;
344
+ background-color: var(--neutral-800);
345
+ border-radius: var(--border-radius);
346
+ overflow-x: auto;
347
+ position: relative;
348
+ }
349
+
350
+ pre code {
351
+ font-family: 'JetBrains Mono', 'Fira Code', Consolas, monospace;
352
+ font-size: 0.9rem;
353
+ color: #e5e7eb;
354
+ padding: 0;
355
+ background: none;
356
+ }
357
+
358
+ .copy-button {
359
+ position: absolute;
360
+ top: 0.5rem;
361
+ right: 0.5rem;
362
+ padding: 0.25rem 0.5rem;
363
+ background-color: rgba(255, 255, 255, 0.1);
364
+ color: rgba(255, 255, 255, 0.6);
365
+ border: none;
366
+ border-radius: var(--border-radius-sm);
367
+ font-size: 0.75rem;
368
+ cursor: pointer;
369
+ transition: var(--transition-base);
370
+ display: flex;
371
+ align-items: center;
372
+ gap: 0.25rem;
373
+ }
374
+
375
+ .copy-button:hover {
376
+ background-color: rgba(255, 255, 255, 0.2);
377
+ color: rgba(255, 255, 255, 0.9);
378
+ }
379
+
380
+ /* 代码执行结果样式 */
381
+ .code-result {
382
+ margin-top: 0.5rem;
383
+ padding: 0.75rem;
384
+ background-color: var(--neutral-900);
385
+ border-radius: var(--border-radius);
386
+ color: white;
387
+ font-family: 'JetBrains Mono', 'Fira Code', Consolas, monospace;
388
+ font-size: 0.85rem;
389
+ white-space: pre-wrap;
390
+ }
391
+
392
+ /* 内联代码样式 */
393
+ code:not(pre code) {
394
+ font-family: 'JetBrains Mono', 'Fira Code', Consolas, monospace;
395
+ font-size: 0.9em;
396
+ color: var(--primary-color);
397
+ background-color: var(--neutral-100);
398
+ padding: 0.2em 0.4em;
399
+ border-radius: 3px;
400
+ }
401
+
402
+ /* 参考资料样式 */
403
+ .reference-container {
404
+ margin-top: 1rem;
405
+ border-top: 1px dashed var(--neutral-300);
406
+ padding-top: 0.75rem;
407
+ }
408
+
409
+ .reference-toggle {
410
+ color: var(--neutral-600);
411
+ font-size: 0.85rem;
412
+ font-weight: 500;
413
+ cursor: pointer;
414
+ display: flex;
415
+ align-items: center;
416
+ transition: var(--transition-base);
417
+ }
418
+
419
+ .reference-toggle:hover {
420
+ color: var(--secondary-color);
421
+ }
422
+
423
+ .reference-toggle i {
424
+ margin-right: 0.35rem;
425
+ font-size: 0.9rem;
426
+ }
427
+
428
+ .reference-content {
429
+ display: none;
430
+ margin-top: 0.75rem;
431
+ padding: 0.75rem;
432
+ background-color: var(--neutral-100);
433
+ border-radius: var(--border-radius);
434
+ font-size: 0.85rem;
435
+ }
436
+
437
+ .reference-item {
438
+ margin-bottom: 0.75rem;
439
+ padding-bottom: 0.75rem;
440
+ border-bottom: 1px solid var(--neutral-200);
441
+ }
442
+
443
+ .reference-item:last-child {
444
+ margin-bottom: 0;
445
+ padding-bottom: 0;
446
+ border-bottom: none;
447
+ }
448
+
449
+ .reference-item-source {
450
+ color: var(--neutral-600);
451
+ font-size: 0.8rem;
452
+ margin-top: 0.25rem;
453
+ }
454
+
455
+ /* 欢迎消息样式 */
456
+ .welcome-message {
457
+ display: flex;
458
+ flex-direction: column;
459
+ align-items: center;
460
+ text-align: center;
461
+ margin-bottom: 1.5rem;
462
+ }
463
+
464
+ .welcome-icon {
465
+ width: 64px;
466
+ height: 64px;
467
+ background: linear-gradient(135deg, var(--secondary-color), var(--secondary-light));
468
+ border-radius: 50%;
469
+ display: flex;
470
+ align-items: center;
471
+ justify-content: center;
472
+ margin-bottom: 1rem;
473
+ color: white;
474
+ font-size: 2rem;
475
+ box-shadow: 0 4px 12px rgba(74, 108, 253, 0.25);
476
+ }
477
+
478
+ .welcome-title {
479
+ font-size: 1.5rem;
480
+ font-weight: 600;
481
+ margin-bottom: 0.5rem;
482
+ color: var(--primary-color);
483
+ }
484
+
485
+ .welcome-description {
486
+ color: var(--neutral-700);
487
+ max-width: 400px;
488
+ margin-bottom: 0;
489
+ }
490
+
491
+ /* 思维导图和可视化插件内容样式 */
492
+ .visualization-result, .mindmap-result {
493
+ padding: 1.5rem;
494
+ text-align: center;
495
+ }
496
+
497
+ .visualization-image, .mindmap-image {
498
+ max-width: 100%;
499
+ border-radius: var(--border-radius);
500
+ box-shadow: var(--shadow);
501
+ }
502
+
503
+ /* 响应式样式 */
504
+ @media (max-width: 1200px) {
505
+ .main-container.with-plugin .chat-container {
506
+ max-width: 320px;
507
+ }
508
+ }
509
+
510
+ @media (max-width: 992px) {
511
+ .main-container {
512
+ flex-direction: column;
513
+ padding: 1rem;
514
+ }
515
+
516
+ .main-container.with-plugin .chat-container {
517
+ max-width: 100%;
518
+ margin-bottom: 1rem;
519
+ height: 40vh;
520
+ }
521
+
522
+ .plugin-container {
523
+ margin-left: 0;
524
+ height: calc(60vh - 32px);
525
+ }
526
+ }
527
+
528
+ @media (max-width: 768px) {
529
+ .main-container {
530
+ padding: 0.75rem;
531
+ }
532
+
533
+ .message {
534
+ max-width: 95%;
535
+ }
536
+ }
537
+
538
+ /* 增强的卡塔赫和Markdown样式 */
539
+ .katex {
540
+ font-size: 1.1em;
541
+ }
542
+
543
+ .message h1, .message h2, .message h3,
544
+ .message h4, .message h5, .message h6 {
545
+ margin-top: 1em;
546
+ margin-bottom: 0.5em;
547
+ line-height: 1.3;
548
+ }
549
+
550
+ .message h1 {
551
+ font-size: 1.6em;
552
+ border-bottom: 1px solid var(--neutral-200);
553
+ padding-bottom: 0.3em;
554
+ }
555
+
556
+ .message h2 {
557
+ font-size: 1.4em;
558
+ border-bottom: 1px solid var(--neutral-200);
559
+ padding-bottom: 0.3em;
560
+ }
561
+
562
+ .message h3 {
563
+ font-size: 1.2em;
564
+ }
565
+
566
+ .message h4 {
567
+ font-size: 1.1em;
568
+ }
569
+
570
+ .message h5, .message h6 {
571
+ font-size: 1em;
572
+ }
573
+
574
+ .message p {
575
+ margin: 0.5em 0;
576
+ }
577
+
578
+ .message ul, .message ol {
579
+ margin: 0.5em 0;
580
+ padding-left: 1.5em;
581
+ }
582
+
583
+ .message li {
584
+ margin: 0.25em 0;
585
+ }
586
+
587
+ .message blockquote {
588
+ margin: 0.5em 0;
589
+ padding-left: 1em;
590
+ border-left: 4px solid var(--neutral-300);
591
+ color: var(--neutral-700);
592
+ }
593
+
594
+ .message img {
595
+ max-width: 100%;
596
+ border-radius: var(--border-radius);
597
+ }
598
+
599
+ .message table {
600
+ border-collapse: collapse;
601
+ margin: 1em 0;
602
+ width: 100%;
603
+ }
604
+
605
+ .message table th,
606
+ .message table td {
607
+ border: 1px solid var(--neutral-300);
608
+ padding: 0.5em;
609
+ }
610
+
611
+ .message table th {
612
+ background-color: var(--neutral-100);
613
+ font-weight: 600;
614
+ }
615
+
616
+ .message table tr:nth-child(even) {
617
+ background-color: var(--neutral-50);
618
+ }
619
+
620
+ /* 动画效果 */
621
+ @keyframes fadeIn {
622
+ from { opacity: 0; transform: translateY(10px); }
623
+ to { opacity: 1; transform: translateY(0); }
624
+ }
625
+
626
+ .message {
627
+ animation: fadeIn 0.3s ease forwards;
628
+ }
629
+
630
+ /* 输入提示区域 */
631
+ .input-suggestions {
632
+ position: absolute;
633
+ bottom: 100%;
634
+ left: 0;
635
+ right: 0;
636
+ background-color: white;
637
+ border-top-left-radius: var(--border-radius-lg);
638
+ border-top-right-radius: var(--border-radius-lg);
639
+ box-shadow: var(--shadow-md);
640
+ padding: 0.75rem;
641
+ display: none;
642
+ border: 1px solid var(--neutral-200);
643
+ border-bottom: none;
644
+ }
645
+
646
+ .suggestion-title {
647
+ font-size: 0.85rem;
648
+ font-weight: 600;
649
+ color: var(--neutral-700);
650
+ margin-bottom: 0.5rem;
651
+ }
652
+
653
+ .suggestion-buttons {
654
+ display: flex;
655
+ flex-wrap: wrap;
656
+ gap: 0.5rem;
657
+ }
658
+
659
+ .suggestion-button {
660
+ padding: 0.5rem 0.75rem;
661
+ background-color: var(--neutral-100);
662
+ border: 1px solid var(--neutral-200);
663
+ border-radius: var(--border-radius);
664
+ font-size: 0.85rem;
665
+ color: var(--neutral-800);
666
+ cursor: pointer;
667
+ transition: var(--transition-base);
668
+ }
669
+
670
+ .suggestion-button:hover {
671
+ background-color: var(--secondary-light);
672
+ color: white;
673
+ border-color: var(--secondary-light);
674
+ }
675
+
676
+ /* 打字机效果 */
677
+ .typing-indicator {
678
+ display: inline-block;
679
+ margin-left: 5px;
680
+ }
681
+
682
+ .typing-dot {
683
+ display: inline-block;
684
+ width: 6px;
685
+ height: 6px;
686
+ border-radius: 50%;
687
+ background-color: var(--neutral-500);
688
+ margin-right: 3px;
689
+ animation: typingDot 1.4s infinite ease-in-out;
690
+ }
691
+
692
+ .typing-dot:nth-child(1) { animation-delay: 0s; }
693
+ .typing-dot:nth-child(2) { animation-delay: 0.2s; }
694
+ .typing-dot:nth-child(3) { animation-delay: 0.4s; }
695
+
696
+ @keyframes typingDot {
697
+ 0%, 60%, 100% { transform: translateY(0); }
698
+ 30% { transform: translateY(-5px); }
699
+ }
700
+ </style>
701
+ </head>
702
+ <body>
703
+ <header class="header">
704
+ <div class="header-content">
705
+ <h1><i class="bi bi-mortarboard"></i> {{ agent_name }}</h1>
706
+ <div>
707
+ <span class="badge">AI学习助手</span>
708
+ </div>
709
+ </div>
710
+ </header>
711
+
712
+ <div class="main-container" id="main-container">
713
+ <div class="chat-container">
714
+ <div class="chat-messages" id="chat-messages">
715
+ <div class="welcome-message">
716
+ <div class="welcome-icon">
717
+ <i class="bi bi-robot"></i>
718
+ </div>
719
+ <h2 class="welcome-title">欢迎使用 {{ agent_name }}</h2>
720
+ {% if agent_description %}
721
+ <p class="welcome-description">{{ agent_description }}</p>
722
+ {% else %}
723
+ <p class="welcome-description">我是您的AI学习助手,有任何问题都可以随时向我提问</p>
724
+ {% endif %}
725
+ </div>
726
+
727
+ <div class="message bot-message">
728
+ <div class="message-content">
729
+ <p>您好!我是{{ agent_name }},很高兴能够帮助您学习。请问有什么我可以协助您的问题吗?</p>
730
+ </div>
731
+ </div>
732
+ </div>
733
+
734
+ <div class="input-container">
735
+ <div class="input-suggestions" id="input-suggestions">
736
+ <div class="suggestion-title">推荐问题:</div>
737
+ <div class="suggestion-buttons">
738
+ <button class="suggestion-button">介绍一下这门课程的主要内容</button>
739
+ <button class="suggestion-button">这门课程有哪些重点知识?</button>
740
+ <button class="suggestion-button">请给我一些学习建议</button>
741
+ </div>
742
+ </div>
743
+ <div class="input-row">
744
+ <textarea class="input-field" id="user-input" placeholder="输入您的问题..." rows="2"></textarea>
745
+ <button class="send-button" id="send-button">
746
+ <i class="bi bi-send"></i>
747
+ </button>
748
+ </div>
749
+ </div>
750
+ </div>
751
+
752
+ <!-- 代码执行插件 -->
753
+ <div class="plugin-container code-plugin" id="code-plugin">
754
+ <div class="plugin-header">
755
+ <h3 class="plugin-title"><i class="bi bi-code-square"></i> Python代码执行</h3>
756
+ <button class="plugin-close" id="close-code-plugin">
757
+ <i class="bi bi-x-lg"></i>
758
+ </button>
759
+ </div>
760
+ <div class="plugin-content">
761
+ <iframe id="code-execution-frame" src="" style="width: 100%; height: 100%; border: none;"></iframe>
762
+ </div>
763
+ </div>
764
+
765
+ <!-- 3D可视化插件 -->
766
+ <div class="plugin-container visualization-plugin" id="visualization-plugin">
767
+ <div class="plugin-header">
768
+ <h3 class="plugin-title"><i class="bi bi-graph-up"></i> 3D可视化</h3>
769
+ <button class="plugin-close" id="close-visualization-plugin">
770
+ <i class="bi bi-x-lg"></i>
771
+ </button>
772
+ </div>
773
+ <div class="plugin-content">
774
+ <div class="visualization-result" id="visualization-result">
775
+ <div class="text-center py-4">
776
+ <div class="spinner-border text-primary" role="status">
777
+ <span class="visually-hidden">加载中...</span>
778
+ </div>
779
+ <p class="mt-3">正在准备3D可视化...</p>
780
+ </div>
781
+ </div>
782
+ </div>
783
+ </div>
784
+
785
+ <!-- 思维导图插件 -->
786
+ <div class="plugin-container mindmap-plugin" id="mindmap-plugin">
787
+ <div class="plugin-header">
788
+ <h3 class="plugin-title"><i class="bi bi-diagram-3"></i> 思维导图</h3>
789
+ <button class="plugin-close" id="close-mindmap-plugin">
790
+ <i class="bi bi-x-lg"></i>
791
+ </button>
792
+ </div>
793
+ <div class="plugin-content">
794
+ <div class="mindmap-result" id="mindmap-result">
795
+ <div class="text-center py-4">
796
+ <div class="spinner-border text-primary" role="status">
797
+ <span class="visually-hidden">加载中...</span>
798
+ </div>
799
+ <p class="mt-3">正在生成思维导图...</p>
800
+ </div>
801
+ </div>
802
+ </div>
803
+ </div>
804
+ </div>
805
+
806
+ <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.js"></script>
807
+ <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/contrib/auto-render.min.js"></script>
808
+ <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/markdown-it.min.js"></script>
809
+ <script>
810
+ // 全局变量
811
+ const agentId = "{{ agent_id }}";
812
+ const token = "{{ token }}";
813
+ let executionContext = null;
814
+ const mainContainer = document.getElementById('main-container');
815
+ let isTyping = false;
816
+
817
+ // DOM元素
818
+ const chatMessages = document.getElementById('chat-messages');
819
+ const userInput = document.getElementById('user-input');
820
+ const sendButton = document.getElementById('send-button');
821
+
822
+ // 代码执行插件元素
823
+ const codePlugin = document.getElementById('code-plugin');
824
+ const closeCodePlugin = document.getElementById('close-code-plugin');
825
+ const codeExecutionFrame = document.getElementById('code-execution-frame');
826
+
827
+ // 3D可视化插件元素
828
+ const visualizationPlugin = document.getElementById('visualization-plugin');
829
+ const closeVisualizationPlugin = document.getElementById('close-visualization-plugin');
830
+ const visualizationResult = document.getElementById('visualization-result');
831
+
832
+ // 思维导图插件元素
833
+ const mindmapPlugin = document.getElementById('mindmap-plugin');
834
+ const closeMindmapPlugin = document.getElementById('close-mindmap-plugin');
835
+ const mindmapResult = document.getElementById('mindmap-result');
836
+
837
+ // 输入建议
838
+ const inputSuggestions = document.getElementById('input-suggestions');
839
+
840
+ // Markdown-it 实例
841
+ const md = window.markdownit({
842
+ html: false,
843
+ linkify: true,
844
+ typographer: true,
845
+ breaks: true,
846
+ highlight: function (str, lang) {
847
+ if (lang && hljs.getLanguage(lang)) {
848
+ try {
849
+ const highlighted = hljs.highlight(str, { language: lang }).value;
850
+ return `<pre><code class="hljs language-${lang}">${highlighted}</code><button class="copy-button" onclick="copyToClipboard(this)"><i class="bi bi-clipboard"></i> 复制</button></pre>`;
851
+ } catch (__) {}
852
+ }
853
+ return `<pre><code class="hljs">${md.utils.escapeHtml(str)}</code><button class="copy-button" onclick="copyToClipboard(this)"><i class="bi bi-clipboard"></i> 复制</button></pre>`;
854
+ }
855
+ });
856
+
857
+ // 页面加载完成后执行
858
+ document.addEventListener('DOMContentLoaded', function() {
859
+ // 初始化发送按钮事件
860
+ sendButton.addEventListener('click', sendMessage);
861
+
862
+ // 初始化输入框回车事件
863
+ userInput.addEventListener('keydown', function(e) {
864
+ if (e.key === 'Enter' && !e.shiftKey) {
865
+ e.preventDefault();
866
+ sendMessage();
867
+ }
868
+ });
869
+
870
+ // 初始化问题建议按钮
871
+ document.querySelectorAll('.suggestion-button').forEach(button => {
872
+ button.addEventListener('click', function() {
873
+ userInput.value = this.textContent;
874
+ sendMessage();
875
+ });
876
+ });
877
+
878
+ // 初始化插件关闭事件
879
+ closeCodePlugin.addEventListener('click', () => {
880
+ codePlugin.style.display = 'none';
881
+ // 清空iframe源以停止任何运行中的代码
882
+ codeExecutionFrame.src = '';
883
+ updateMainContainerLayout();
884
+ });
885
+
886
+ closeVisualizationPlugin.addEventListener('click', () => {
887
+ visualizationPlugin.style.display = 'none';
888
+ updateMainContainerLayout();
889
+ });
890
+
891
+ closeMindmapPlugin.addEventListener('click', () => {
892
+ mindmapPlugin.style.display = 'none';
893
+ updateMainContainerLayout();
894
+ });
895
+
896
+ // 焦点事件
897
+ userInput.addEventListener('focus', function() {
898
+ // inputSuggestions.style.display = 'block';
899
+ });
900
+
901
+ userInput.addEventListener('blur', function(e) {
902
+ // 延迟执行,以便点击建议按钮事件先触发
903
+ setTimeout(() => {
904
+ // inputSuggestions.style.display = 'none';
905
+ }, 100);
906
+ });
907
+ });
908
+
909
+ // 更新主容器布局
910
+ function updateMainContainerLayout() {
911
+ // 检查是否有任何插件处于显示状态
912
+ const isAnyPluginVisible =
913
+ codePlugin.style.display === 'flex' ||
914
+ visualizationPlugin.style.display === 'flex' ||
915
+ mindmapPlugin.style.display === 'flex';
916
+
917
+ // 更新主容器类名
918
+ if (isAnyPluginVisible) {
919
+ mainContainer.classList.add('with-plugin');
920
+ } else {
921
+ mainContainer.classList.remove('with-plugin');
922
+ }
923
+ }
924
+
925
+ // 发送消息
926
+ async function sendMessage() {
927
+ const message = userInput.value.trim();
928
+ if (!message || isTyping) return;
929
+
930
+ // 添加用户消息
931
+ addMessage(message, true);
932
+
933
+ // 清空输入框
934
+ userInput.value = '';
935
+
936
+ // 禁用发送按钮
937
+ sendButton.disabled = true;
938
+ isTyping = true;
939
+
940
+ // 添加机器人正在输入的指示
941
+ const typingIndicator = addTypingIndicator();
942
+
943
+ try {
944
+ // 发送请求
945
+ const response = await fetch(`/api/student/chat/${agentId}`, {
946
+ method: 'POST',
947
+ headers: {
948
+ 'Content-Type': 'application/json'
949
+ },
950
+ body: JSON.stringify({
951
+ message: message,
952
+ token: token
953
+ })
954
+ });
955
+
956
+ const data = await response.json();
957
+
958
+ // 移除输入指示器
959
+ if (typingIndicator) {
960
+ typingIndicator.remove();
961
+ }
962
+
963
+ if (data.success) {
964
+ // 处理回复内容,移除参考来源部分
965
+ const processedContent = processResponseContent(data.message);
966
+
967
+ // 添加机器人回复
968
+ const messageElement = addMessage(processedContent, false);
969
+
970
+ // 添加参考信息(如果有)
971
+ if (data.references && data.references.length > 0) {
972
+ addReferences(messageElement, data.references);
973
+ }
974
+
975
+ // 检查是否需要显示工具
976
+ if (data.tools && data.tools.length > 0) {
977
+ // 隐藏所有插件
978
+ hideAllPlugins();
979
+
980
+ // 使用新的插件激活函数
981
+ activatePlugins(data.message, data.tools);
982
+ }
983
+ } else {
984
+ // 添加错误消息
985
+ addMessage(`错误: ${data.message}`, false);
986
+ }
987
+ } catch (error) {
988
+ console.error('发送请求出错:', error);
989
+ // 移除输入指示器
990
+ if (typingIndicator) {
991
+ typingIndicator.remove();
992
+ }
993
+ addMessage('发送请求时出错,请重试', false);
994
+ } finally {
995
+ // 恢复发送按钮
996
+ sendButton.disabled = false;
997
+ isTyping = false;
998
+ }
999
+ }
1000
+
1001
+ // 添加打字指示器
1002
+ function addTypingIndicator() {
1003
+ const botMessage = document.createElement('div');
1004
+ botMessage.className = 'message bot-message';
1005
+
1006
+ const content = document.createElement('div');
1007
+ content.className = 'message-content';
1008
+ content.innerHTML = `
1009
+ <p>
1010
+ <span class="typing-indicator">
1011
+ <span class="typing-dot"></span>
1012
+ <span class="typing-dot"></span>
1013
+ <span class="typing-dot"></span>
1014
+ </span>
1015
+ </p>
1016
+ `;
1017
+
1018
+ botMessage.appendChild(content);
1019
+ chatMessages.appendChild(botMessage);
1020
+
1021
+ // 滚动到底部
1022
+ chatMessages.scrollTop = chatMessages.scrollHeight;
1023
+
1024
+ return botMessage;
1025
+ }
1026
+
1027
+ // 处理回复内容,移除参考来源部分
1028
+ function processResponseContent(content) {
1029
+ // 移除 "===参考来源开始===" 到 "===参考来源结束===" 之间的内容
1030
+ return content.replace(/===参考来源开始===[\s\S]*?===参考来源结束===/, '');
1031
+ }
1032
+
1033
+ // 添加参考信息显示函数
1034
+ function addReferences(messageElement, references) {
1035
+ // 创建参考容器
1036
+ const referenceContainer = document.createElement('div');
1037
+ referenceContainer.className = 'reference-container';
1038
+
1039
+ // 创建参考切换按钮
1040
+ const toggleButton = document.createElement('div');
1041
+ toggleButton.className = 'reference-toggle';
1042
+ toggleButton.innerHTML = '<i class="bi bi-journal-text"></i> 参考来源';
1043
+
1044
+ // 创建参考内容
1045
+ const referenceContent = document.createElement('div');
1046
+ referenceContent.className = 'reference-content';
1047
+
1048
+ // 添加参考项
1049
+ references.forEach(ref => {
1050
+ const refItem = document.createElement('div');
1051
+ refItem.className = 'reference-item';
1052
+
1053
+ refItem.innerHTML = `
1054
+ <div><strong>[${ref.index}]</strong> ${ref.summary}</div>
1055
+ <div class="reference-item-source">来源: ${ref.file_name}</div>
1056
+ `;
1057
+
1058
+ referenceContent.appendChild(refItem);
1059
+ });
1060
+
1061
+ // 添加切换功能
1062
+ toggleButton.addEventListener('click', () => {
1063
+ if (referenceContent.style.display === 'block') {
1064
+ referenceContent.style.display = 'none';
1065
+ toggleButton.innerHTML = '<i class="bi bi-journal-text"></i> 参考来源';
1066
+ } else {
1067
+ referenceContent.style.display = 'block';
1068
+ toggleButton.innerHTML = '<i class="bi bi-journal-arrow-up"></i> 收起参考来源';
1069
+ }
1070
+ });
1071
+
1072
+ // 组装并添加到消息
1073
+ referenceContainer.appendChild(toggleButton);
1074
+ referenceContainer.appendChild(referenceContent);
1075
+ messageElement.appendChild(referenceContainer);
1076
+ }
1077
+
1078
+ // 添加消息函数
1079
+ function addMessage(content, isUser) {
1080
+ const messageDiv = document.createElement('div');
1081
+ messageDiv.className = isUser ? 'message user-message' : 'message bot-message';
1082
+
1083
+ const messageContent = document.createElement('div');
1084
+ messageContent.className = 'message-content';
1085
+
1086
+ if (isUser) {
1087
+ // 直接显示用户消息,仅替换换行符
1088
+ messageContent.innerHTML = `<p>${content.replace(/\n/g, '<br>')}</p>`;
1089
+ } else {
1090
+ // 使用 Markdown-it 解析机器人消息
1091
+ messageContent.innerHTML = md.render(content);
1092
+
1093
+ // 渲染LaTeX公式
1094
+ setTimeout(() => {
1095
+ try {
1096
+ renderMathInElement(messageContent, {
1097
+ delimiters: [
1098
+ {left: '$$', right: '$$', display: true},
1099
+ {left: '$', right: '$', display: false},
1100
+ {left: '\\(', right: '\\)', display: false},
1101
+ {left: '\\[', right: '\\]', display: true}
1102
+ ],
1103
+ throwOnError: false
1104
+ });
1105
+ } catch (e) {
1106
+ console.error('渲染LaTeX出错:', e);
1107
+ }
1108
+ }, 0);
1109
+ }
1110
+
1111
+ messageDiv.appendChild(messageContent);
1112
+ chatMessages.appendChild(messageDiv);
1113
+
1114
+ // 滚动到底部
1115
+ chatMessages.scrollTop = chatMessages.scrollHeight;
1116
+
1117
+ return messageDiv;
1118
+ }
1119
+
1120
+ // 复制代码到剪贴板
1121
+ function copyToClipboard(button) {
1122
+ const codeBlock = button.parentNode.querySelector('code');
1123
+ const text = codeBlock.textContent;
1124
+
1125
+ navigator.clipboard.writeText(text).then(() => {
1126
+ button.innerHTML = '<i class="bi bi-check"></i> 已复制';
1127
+
1128
+ setTimeout(() => {
1129
+ button.innerHTML = '<i class="bi bi-clipboard"></i> 复制';
1130
+ }, 2000);
1131
+ }).catch(err => {
1132
+ console.error('复制失败:', err);
1133
+ button.innerHTML = '<i class="bi bi-exclamation-triangle"></i> 失败';
1134
+
1135
+ setTimeout(() => {
1136
+ button.innerHTML = '<i class="bi bi-clipboard"></i> 复制';
1137
+ }, 2000);
1138
+ });
1139
+ }
1140
+
1141
+ // 隐藏所有插件
1142
+ function hideAllPlugins() {
1143
+ codePlugin.style.display = 'none';
1144
+ visualizationPlugin.style.display = 'none';
1145
+ mindmapPlugin.style.display = 'none';
1146
+ updateMainContainerLayout();
1147
+ }
1148
+
1149
+ // 提取代码块
1150
+ function extractCodeBlocks(message) {
1151
+ const codeBlocks = [];
1152
+ const codeRegex = /```python\n([\s\S]*?)\n```/g;
1153
+
1154
+ let match;
1155
+ while ((match = codeRegex.exec(message)) !== null) {
1156
+ codeBlocks.push(match[1]);
1157
+ }
1158
+
1159
+ return codeBlocks;
1160
+ }
1161
+
1162
+ // 提取3D可视化函数代码
1163
+ function extract3DVisualizationCode(message) {
1164
+ // 尝试提取函数代码块
1165
+ const codeRegex = /```python\s*(import[\s\S]*?def create_3d_plot\(\):[\s\S]*?return[\s\S]*?})\s*```/i;
1166
+ const match = codeRegex.exec(message);
1167
+
1168
+ if (match) {
1169
+ return match[1].trim();
1170
+ }
1171
+
1172
+ return null;
1173
+ }
1174
+
1175
+ // 提取思维导图内容
1176
+ function extractMindmapContent(message) {
1177
+ // 尝试提取@startmindmap和@endmindmap之间的内容
1178
+ const mindmapRegex = /@startmindmap\n([\s\S]*?)@endmindmap/;
1179
+ const match = mindmapRegex.exec(message);
1180
+
1181
+ if (match) {
1182
+ return `@startmindmap\n${match[1]}\n@endmindmap`;
1183
+ }
1184
+
1185
+ return null;
1186
+ }
1187
+
1188
+ // 激活代码执行插件
1189
+ function activateCodePlugin(message) {
1190
+ // 提取代码块
1191
+ const codeBlocks = extractCodeBlocks(message);
1192
+
1193
+ if (codeBlocks.length > 0) {
1194
+ const isAlreadyVisible = codePlugin.style.display === 'flex';
1195
+
1196
+ // 显示插件容器
1197
+ codePlugin.style.display = 'flex';
1198
+ updateMainContainerLayout();
1199
+
1200
+ // 获取iframe
1201
+ const iframe = document.getElementById('code-execution-frame');
1202
+
1203
+ // 如果插件已经可见且iframe已加载
1204
+ if (isAlreadyVisible && iframe.contentWindow) {
1205
+ // 发送新代码到现有iframe
1206
+ iframe.contentWindow.postMessage({
1207
+ type: 'setCode',
1208
+ code: codeBlocks[0]
1209
+ }, '*');
1210
+ } else {
1211
+ // 设置iframe源
1212
+ let src = '/code_execution.html';
1213
+ if (codeBlocks.length > 0) {
1214
+ src += `?code=${encodeURIComponent(codeBlocks[0])}`;
1215
+ }
1216
+
1217
+ iframe.src = src;
1218
+
1219
+ // 设置iframe加载事件处理程序
1220
+ iframe.onload = function() {
1221
+ if (codeBlocks.length > 0) {
1222
+ iframe.contentWindow.postMessage({
1223
+ type: 'setCode',
1224
+ code: codeBlocks[0]
1225
+ }, '*');
1226
+ }
1227
+ };
1228
+ }
1229
+ }
1230
+ }
1231
+
1232
+ // 激活3D可视化插件
1233
+ function activate3DVisualization(message) {
1234
+ const code = extract3DVisualizationCode(message);
1235
+
1236
+ if (code) {
1237
+ // 显示插件容器
1238
+ visualizationPlugin.style.display = 'flex';
1239
+ updateMainContainerLayout();
1240
+
1241
+ // 显示加载状态
1242
+ visualizationResult.innerHTML = `
1243
+ <div class="text-center py-4">
1244
+ <div class="spinner-border" style="color: var(--secondary-color);" role="status">
1245
+ <span class="visually-hidden">生成中...</span>
1246
+ </div>
1247
+ <p class="mt-3">正在生成3D图形,请稍候...</p>
1248
+ </div>
1249
+ `;
1250
+
1251
+ // 自动生成可视化
1252
+ fetch('/api/visualization/3d-surface', {
1253
+ method: 'POST',
1254
+ headers: {
1255
+ 'Content-Type': 'application/json'
1256
+ },
1257
+ body: JSON.stringify({ code: code })
1258
+ })
1259
+ .then(response => response.json())
1260
+ .then(data => {
1261
+ if (data.success) {
1262
+ // 直接嵌入HTML
1263
+ visualizationResult.innerHTML = `
1264
+ <div class="visualization-iframe-container" style="width:100%; height:500px;">
1265
+ <iframe src="${data.html_url}" style="width:100%; height:100%; border:none;"></iframe>
1266
+ </div>
1267
+ <div class="text-center mt-3">
1268
+ <p>3D图形生成成功</p>
1269
+ <a href="${data.html_url}" class="btn btn-sm btn-outline-primary" target="_blank">
1270
+ <i class="bi bi-arrows-fullscreen"></i> 全屏查看
1271
+ </a>
1272
+ </div>
1273
+ `;
1274
+ } else {
1275
+ visualizationResult.innerHTML = `
1276
+ <div class="alert alert-danger">
1277
+ <i class="bi bi-exclamation-triangle-fill me-2"></i>
1278
+ 生成失败: ${data.message}
1279
+ </div>
1280
+ `;
1281
+ }
1282
+ })
1283
+ .catch(error => {
1284
+ console.error('生成3D图形出错:', error);
1285
+ visualizationResult.innerHTML = `
1286
+ <div class="alert alert-danger">
1287
+ <i class="bi bi-exclamation-triangle-fill me-2"></i>
1288
+ 生成3D图形时发生错误,请重试
1289
+ </div>
1290
+ `;
1291
+ });
1292
+ }
1293
+ }
1294
+
1295
+ // 激活思维导图插件
1296
+ function activateMindmap(message) {
1297
+ const content = extractMindmapContent(message);
1298
+
1299
+ if (content) {
1300
+ // 显示插件容器
1301
+ mindmapPlugin.style.display = 'flex';
1302
+ updateMainContainerLayout();
1303
+
1304
+ // 显示加载状态
1305
+ mindmapResult.innerHTML = `
1306
+ <div class="text-center py-4">
1307
+ <div class="spinner-border" style="color: var(--secondary-color);" role="status">
1308
+ <span class="visually-hidden">生成中...</span>
1309
+ </div>
1310
+ <p class="mt-3">正在生成思维导图,请稍候...</p>
1311
+ </div>
1312
+ `;
1313
+
1314
+ // 自动生成思维导图
1315
+ fetch('/api/visualization/mindmap', {
1316
+ method: 'POST',
1317
+ headers: {
1318
+ 'Content-Type': 'application/json'
1319
+ },
1320
+ body: JSON.stringify({ content: content })
1321
+ })
1322
+ .then(response => response.json())
1323
+ .then(data => {
1324
+ if (data.success) {
1325
+ mindmapResult.innerHTML = `
1326
+ <div class="text-center">
1327
+ <img src="${data.url}" class="mindmap-image" alt="思维导图">
1328
+ <p class="mt-3">生成成功!</p>
1329
+ <a href="${data.url}" class="btn btn-sm btn-outline-primary mt-2" target="_blank">
1330
+ <i class="bi bi-download"></i> 下载图片
1331
+ </a>
1332
+ </div>
1333
+ `;
1334
+ } else {
1335
+ mindmapResult.innerHTML = `
1336
+ <div class="alert alert-danger">
1337
+ <i class="bi bi-exclamation-triangle-fill me-2"></i>
1338
+ 生成失败: ${data.message}
1339
+ </div>
1340
+ `;
1341
+ }
1342
+ })
1343
+ .catch(error => {
1344
+ console.error('生成思维导图出错:', error);
1345
+ mindmapResult.innerHTML = `
1346
+ <div class="alert alert-danger">
1347
+ <i class="bi bi-exclamation-triangle-fill me-2"></i>
1348
+ 生成思维导图时发生错误,请重试
1349
+ </div>
1350
+ `;
1351
+ });
1352
+ }
1353
+ }
1354
+
1355
+ // 根据消息内容激活适当的插件
1356
+ function activatePlugins(message, tools) {
1357
+ // 检查并激活代码执行插件
1358
+ if (tools.includes('code') && message.includes('```python')) {
1359
+ activateCodePlugin(message);
1360
+ }
1361
+
1362
+ // 检查并激活3D可视化插件
1363
+ if (tools.includes('visualization') &&
1364
+ (message.includes('def create_3d_plot') ||
1365
+ message.includes('3D') || message.includes('可视化'))) {
1366
+ activate3DVisualization(message);
1367
+ }
1368
+
1369
+ // 检查并激活思维导图插件
1370
+ if (tools.includes('mindmap') &&
1371
+ (message.includes('@startmindmap') ||
1372
+ message.includes('思维导图'))) {
1373
+ activateMindmap(message);
1374
+ }
1375
+ }
1376
+ </script>
1377
+
1378
+ <!-- 添加highlight.js用于代码高亮 -->
1379
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/highlight.min.js"></script>
1380
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/languages/python.min.js"></script>
1381
+ </body>
1382
+ </html>