atlury commited on
Commit
076b6ec
·
verified ·
1 Parent(s): 0abd3ff

Upload 13 files

Browse files
advanced.math.css ADDED
@@ -0,0 +1,422 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* advanced.math.css */
2
+
3
+ /* Reset and Base Styles */
4
+ #advanced-math * {
5
+ box-sizing: border-box;
6
+ font-family: Arial, sans-serif;
7
+ }
8
+
9
+ /* Card Styling */
10
+ .advanced-math-card {
11
+ display: flex;
12
+ gap: 20px;
13
+ background: linear-gradient(135deg, #ffffff, #f1f8e9);
14
+ border-radius: 16px;
15
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05);
16
+ padding: 25px;
17
+ margin: 25px auto;
18
+
19
+ transition: transform 0.3s, box-shadow 0.3s, background 0.3s;
20
+ }
21
+
22
+ .advanced-math-card:hover {
23
+ transform: translateY(-5px);
24
+ box-shadow: 0 6px 25px rgba(0, 0, 0, 0.1);
25
+ background: linear-gradient(135deg, #e0f7fa, #ffffff);
26
+ }
27
+
28
+ /* Flex Layout for Math Options and Chat Window */
29
+ .math-options {
30
+ flex: 1;
31
+ display: flex;
32
+ flex-direction: column;
33
+ gap: 20px;
34
+ }
35
+
36
+ /* Ensure Math Options takes up available vertical space */
37
+ .math-options {
38
+ height: 100%;
39
+ }
40
+
41
+ /* Chat Window Styling */
42
+ .chat-window {
43
+ flex: 2;
44
+ display: flex;
45
+ flex-direction: column;
46
+ gap: 20px;
47
+ }
48
+
49
+ /* Option Sections Styling */
50
+ .option-section {
51
+ background-color: #ffffff;
52
+ padding: 15px;
53
+ border-radius: 12px;
54
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
55
+ transition: background 0.3s, box-shadow 0.3s;
56
+ }
57
+
58
+ .option-section:hover {
59
+ background-color: #f9f9f9;
60
+ }
61
+
62
+ /* Button Styling (Reused from Original) */
63
+ #advanced-math button {
64
+ background: linear-gradient(135deg, #00796B, #004D40);
65
+ border: none;
66
+ color: white;
67
+ padding: 12px 20px;
68
+ font-size: 1em;
69
+ border-radius: 8px;
70
+ cursor: pointer;
71
+ transition: background 0.3s, transform 0.2s, box-shadow 0.3s;
72
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
73
+ }
74
+
75
+ #advanced-math button:hover:not(:disabled) {
76
+ background: linear-gradient(135deg, #005D56, #00332E);
77
+ transform: translateY(-2px);
78
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
79
+ }
80
+
81
+ #advanced-math button:disabled {
82
+ background: linear-gradient(135deg, #A5D6A7, #81C784);
83
+ cursor: not-allowed;
84
+ box-shadow: none;
85
+ }
86
+
87
+ /* Select Dropdown Styling (Reused from Original) */
88
+ #advanced-math select {
89
+ width: 100%;
90
+ padding: 10px 15px;
91
+ border: 1px solid #cccccc;
92
+ border-radius: 8px;
93
+ font-size: 1em;
94
+ background-color: #ffffff;
95
+ transition: border-color 0.3s, box-shadow 0.3s;
96
+ margin-bottom: 15px;
97
+ }
98
+
99
+ #advanced-math select:focus {
100
+ border-color: #00796B;
101
+ box-shadow: 0 0 5px rgba(0, 121, 107, 0.5);
102
+ outline: none;
103
+ }
104
+
105
+ /* Speech Controls Styling */
106
+ #math-speech-controls label {
107
+ display: block;
108
+ margin-bottom: 5px;
109
+ color: #555555;
110
+ font-weight: 500;
111
+ }
112
+
113
+ #math-speech-controls input[type="range"] {
114
+ width: 100%;
115
+ margin-bottom: 15px;
116
+ }
117
+
118
+ /* Logs Container Styling */
119
+ #math-logs-container {
120
+ display: flex;
121
+ flex-direction: column;
122
+ gap: 10px;
123
+ max-height: 200px; /* Set a maximum height for the logs container */
124
+ overflow: hidden; /* Hide any overflow to prevent the container from expanding */
125
+ }
126
+
127
+ #math-logs {
128
+ flex: 1;
129
+ max-height: 200px; /* Adjust as needed */
130
+ border: 1px solid #e0e0e0;
131
+ border-radius: 12px;
132
+ overflow-y: auto; /* Enable vertical scrolling */
133
+ padding: 10px;
134
+ background-color: #f9f9f9;
135
+ font-size: 0.85em;
136
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.05);
137
+ }
138
+
139
+ #math-clear-logs {
140
+ align-self: flex-end;
141
+ background: linear-gradient(135deg, #FF5252, #E53935);
142
+ border: none;
143
+ color: white;
144
+ padding: 8px 16px;
145
+ font-size: 0.9em;
146
+ cursor: pointer;
147
+ border-radius: 8px;
148
+ transition: background 0.3s, transform 0.2s, box-shadow 0.3s;
149
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
150
+ }
151
+
152
+ #math-clear-logs:hover {
153
+ background: linear-gradient(135deg, #E53935, #D32F2F);
154
+ transform: scale(1.02);
155
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
156
+ }
157
+
158
+ /* Chat Box Styling */
159
+ #math-chat-box {
160
+ height: 500px;
161
+ border: 1px solid #e0e0e0;
162
+ border-radius: 12px;
163
+ overflow-y: auto;
164
+ padding: 20px;
165
+ background-color: #ffffff;
166
+ position: relative;
167
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.05);
168
+ margin-bottom: 20px;
169
+ }
170
+
171
+ /* Message Styling */
172
+ #advanced-math .message-container {
173
+ margin-bottom: 20px;
174
+ display: flex;
175
+ }
176
+
177
+ #advanced-math .message {
178
+ padding: 12px 18px;
179
+ border-radius: 20px;
180
+ max-width: 75%;
181
+ position: relative;
182
+ word-wrap: break-word;
183
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
184
+ font-size: 0.95em;
185
+ line-height: 1.4;
186
+ background-color: #f1f8e9;
187
+ transition: background-color 0.3s, box-shadow 0.3s;
188
+ }
189
+
190
+ #advanced-math .user .message {
191
+ background: #DCF8C6;
192
+ border-bottom-right-radius: 0;
193
+ }
194
+
195
+ #advanced-math .assistant .message {
196
+ background: #e3f2fd;
197
+ border-bottom-left-radius: 0;
198
+ }
199
+
200
+ /* Chat Input Container Styling */
201
+ #math-text-input-container {
202
+ display: flex;
203
+ gap: 10px;
204
+ }
205
+
206
+ #math-text-input {
207
+ flex: 1;
208
+ padding: 10px 15px;
209
+ border: 1px solid #cccccc;
210
+ border-radius: 8px;
211
+ font-size: 1em;
212
+ background-color: #ffffff;
213
+ transition: border-color 0.3s, box-shadow 0.3s;
214
+ }
215
+
216
+ #math-text-input:focus {
217
+ border-color: #00796B;
218
+ box-shadow: 0 0 5px rgba(0, 121, 107, 0.5);
219
+ outline: none;
220
+ }
221
+
222
+ #math-submit-button {
223
+ background: linear-gradient(135deg, #00796B, #004D40);
224
+ border: none;
225
+ color: white;
226
+ padding: 10px 20px;
227
+ font-size: 1em;
228
+ border-radius: 8px;
229
+ cursor: pointer;
230
+ transition: background 0.3s, transform 0.2s, box-shadow 0.3s;
231
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
232
+ }
233
+
234
+ #math-submit-button:hover:not(:disabled) {
235
+ background: linear-gradient(135deg, #005D56, #00332E);
236
+ transform: translateY(-2px);
237
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
238
+ }
239
+
240
+ #math-submit-button:disabled {
241
+ background: linear-gradient(135deg, #A5D6A7, #81C784);
242
+ cursor: not-allowed;
243
+ box-shadow: none;
244
+ }
245
+
246
+ /* Microphone and Stop Buttons Styling */
247
+ #math-mic-container {
248
+ display: flex;
249
+ justify-content: center;
250
+ gap: 15px;
251
+ margin-top: 20px;
252
+ }
253
+
254
+ #math-start_button,
255
+ #math-stop_button {
256
+ background: linear-gradient(135deg, #00796B, #004D40);
257
+ border: none;
258
+ color: white;
259
+ padding: 15px;
260
+ border-radius: 50%;
261
+ cursor: pointer;
262
+ transition: background 0.3s, transform 0.2s, box-shadow 0.3s;
263
+ width: 60px;
264
+ height: 60px;
265
+ display: flex;
266
+ justify-content: center;
267
+ align-items: center;
268
+ position: relative;
269
+ overflow: hidden;
270
+ box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
271
+ }
272
+
273
+ #math-stop_button {
274
+ background: linear-gradient(135deg, #E53935, #D32F2F);
275
+ }
276
+
277
+ #math-start_button::before,
278
+ #math-stop_button::before {
279
+ content: '';
280
+ position: absolute;
281
+ width: 200%;
282
+ height: 200%;
283
+ background: rgba(255, 255, 255, 0.3);
284
+ top: 50%;
285
+ left: 50%;
286
+ transform: translate(-50%, -50%) scale(0);
287
+ border-radius: 50%;
288
+ transition: transform 0.5s ease-out;
289
+ }
290
+
291
+ #math-start_button:active::before,
292
+ #math-stop_button:active::before {
293
+ transform: translate(-50%, -50%) scale(1);
294
+ }
295
+
296
+ #math-start_button:disabled,
297
+ #math-stop_button:disabled {
298
+ background: linear-gradient(135deg, #A5D6A7, #81C784);
299
+ cursor: not-allowed;
300
+ box-shadow: none;
301
+ }
302
+
303
+ #math-start_button:hover:not(:disabled),
304
+ #math-stop_button:hover:not(:disabled) {
305
+ transform: scale(1.05);
306
+ box-shadow: 0 6px 15px rgba(0, 0, 0, 0.15);
307
+ }
308
+
309
+ #math-stop_button:hover:not(:disabled) {
310
+ background: linear-gradient(135deg, #D32F2F, #C62828);
311
+ }
312
+
313
+ /* Icon Styling */
314
+ #advanced-math .mic-icon,
315
+ #advanced-math .stop-icon {
316
+ width: 30px;
317
+ height: 30px;
318
+ transition: transform 0.3s;
319
+ fill: #ffffff;
320
+ }
321
+
322
+ /* Animation for Mic Icon */
323
+ #advanced-math .mic-animate {
324
+ animation: pulse 2s infinite;
325
+ }
326
+
327
+ @keyframes pulse {
328
+ 0% {
329
+ transform: scale(1);
330
+ }
331
+ 50% {
332
+ transform: scale(1.2);
333
+ }
334
+ 100% {
335
+ transform: scale(1);
336
+ }
337
+ }
338
+
339
+ /* Chat Stats */
340
+ #math-chat-stats {
341
+ font-size: 0.85em;
342
+ color: #555555;
343
+ text-align: center;
344
+ margin-top: 10px;
345
+ }
346
+
347
+ /* Hidden Class */
348
+ #advanced-math .hidden {
349
+ display: none;
350
+ }
351
+
352
+ /* Scrollbar Styling */
353
+ #math-chat-box::-webkit-scrollbar,
354
+ #math-logs::-webkit-scrollbar {
355
+ width: 8px;
356
+ }
357
+
358
+ #math-chat-box::-webkit-scrollbar-track,
359
+ #math-logs::-webkit-scrollbar-track {
360
+ background: #f1f1f1;
361
+ border-radius: 4px;
362
+ }
363
+
364
+ #math-chat-box::-webkit-scrollbar-thumb,
365
+ #math-logs::-webkit-scrollbar-thumb {
366
+ background: #cccccc;
367
+ border-radius: 4px;
368
+ }
369
+
370
+ #math-chat-box::-webkit-scrollbar-thumb:hover,
371
+ #math-logs::-webkit-scrollbar-thumb:hover {
372
+ background: #a8a8a8;
373
+ }
374
+
375
+ /* Initialize Button Styling (Reused from Original) */
376
+ #math-download {
377
+ flex: 0 0 auto;
378
+ background: linear-gradient(135deg, #00796B, #004D40);
379
+ border: none;
380
+ color: white;
381
+ padding: 10px 20px;
382
+ font-size: 1em;
383
+ border-radius: 8px;
384
+ cursor: pointer;
385
+ transition: background 0.3s, transform 0.2s, box-shadow 0.3s;
386
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
387
+ }
388
+
389
+ #math-download:hover:not(:disabled) {
390
+ background: linear-gradient(135deg, #005D56, #00332E);
391
+ transform: translateY(-2px);
392
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
393
+ }
394
+
395
+ #math-download:disabled {
396
+ background: linear-gradient(135deg, #A5D6A7, #81C784);
397
+ cursor: not-allowed;
398
+ box-shadow: none;
399
+ }
400
+
401
+ /* Configuration Info Styling */
402
+ #math-configuration {
403
+ margin-top: 15px;
404
+ font-size: 0.9em;
405
+ color: #555555;
406
+ }
407
+
408
+ /* Responsive Grid Adjustments */
409
+ @media (max-width: 800px) {
410
+ .advanced-math-card {
411
+ flex-direction: column;
412
+ }
413
+
414
+ /* Adjust Logs Container Height for Smaller Screens */
415
+ #math-logs-container {
416
+ max-height: 200px;
417
+ }
418
+
419
+ #math-logs {
420
+ max-height: 150px;
421
+ }
422
+ }
advanced.math.js ADDED
@@ -0,0 +1,612 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // advanced.math.js
2
+ import * as webllm from "https://esm.run/@mlc-ai/web-llm";
3
+
4
+ // Ensure the script runs after the DOM is fully loaded
5
+ document.addEventListener("DOMContentLoaded", () => {
6
+ // Initialize the Advanced Mathematics & Problem Solving section
7
+ const mathMessages = [
8
+ {
9
+ content: "You are Aged Guru, an intelligent assistant skilled in advanced mathematics and problem solving. Provide insightful and comprehensive answers to complex mathematical questions.",
10
+ role: "system"
11
+ }
12
+ ];
13
+
14
+ const mathAvailableModels = webllm.prebuiltAppConfig.model_list.map(
15
+ (m) => m.model_id
16
+ );
17
+ let mathSelectedModel = "Qwen2.5-Math-1.5B-Instruct-q4f16_1-MLC"; // Default model
18
+
19
+ function mathUpdateEngineInitProgressCallback(report) {
20
+ console.log("Advanced Math Initialize", report.progress);
21
+ // Instead of updating a status span, log the progress
22
+ logMessage(`Model Initialization Progress: ${report.text}`, "system");
23
+ }
24
+
25
+ const mathEngine = new webllm.MLCEngine();
26
+ mathEngine.setInitProgressCallback(mathUpdateEngineInitProgressCallback);
27
+
28
+ let mathIsGenerating = false; // Flag to prevent multiple generations
29
+
30
+ async function mathInitializeWebLLMEngine() {
31
+ logMessage("Model initialization started.", "system");
32
+ document.getElementById("math-loading-spinner").classList.remove("hidden"); // Show spinner
33
+ mathSelectedModel = document.getElementById("math-model-selection").value;
34
+ const config = {
35
+ temperature: 0.7, // Adjusted for more precise answers
36
+ top_p: 0.9
37
+ };
38
+ try {
39
+ await mathEngine.reload(mathSelectedModel, config);
40
+ document.getElementById("math-selected-model").textContent = mathSelectedModel;
41
+ document.getElementById("math-start_button").disabled = false;
42
+ document.getElementById("math-text-input").disabled = false; // Enable text input after initialization
43
+ document.getElementById("math-submit-button").disabled = false; // Enable submit button after initialization
44
+ document.getElementById("math-speech-controls").disabled = false; // Enable speech controls after initialization
45
+ document.getElementById("math-configuration").classList.remove("hidden");
46
+ logMessage("Model initialized successfully.", "system");
47
+ } catch (error) {
48
+ console.error("Error initializing the model:", error);
49
+ alert("Failed to initialize the model. Please try again.");
50
+ logMessage("Failed to initialize the model.", "error");
51
+ } finally {
52
+ document.getElementById("math-loading-spinner").classList.add("hidden"); // Hide spinner
53
+ }
54
+ }
55
+
56
+ async function mathStreamingGenerating(messages, onUpdate, onFinish, onError) {
57
+ if (mathIsGenerating) {
58
+ console.warn("Advanced Math Generation already in progress.");
59
+ return;
60
+ }
61
+ mathIsGenerating = true;
62
+ try {
63
+ let curMessage = "";
64
+ const completion = await mathEngine.chat.completions.create({
65
+ stream: true,
66
+ messages
67
+ });
68
+ for await (const chunk of completion) {
69
+ const curDelta = chunk.choices[0].delta.content;
70
+ if (curDelta) {
71
+ curMessage += curDelta;
72
+ }
73
+ onUpdate(curMessage);
74
+ }
75
+ const finalMessage = await mathEngine.getMessage();
76
+ console.log(`Advanced Math Generated final message: ${finalMessage}`); // Debugging
77
+ onFinish(finalMessage);
78
+ logMessage("Response generated successfully.", "system");
79
+ } catch (err) {
80
+ console.error(err);
81
+ onError(err);
82
+ logMessage("An error occurred during response generation.", "error");
83
+ } finally {
84
+ mathIsGenerating = false;
85
+ }
86
+ }
87
+
88
+ // Flag to track the last input method
89
+ let mathLastInputWasVoice = false;
90
+
91
+ function mathAppendMessage(message) {
92
+ console.log(`Advanced Math Appending message: ${message.content} (Role: ${message.role})`); // Debugging
93
+ const mathChatBox = document.getElementById("math-chat-box");
94
+
95
+ // Check if the assistant's message is already appended to avoid duplication
96
+ if (message.role === "assistant") {
97
+ const existingMessages = mathChatBox.querySelectorAll(".message");
98
+ const lastMessage = existingMessages[existingMessages.length - 1];
99
+ if (lastMessage && lastMessage.textContent === message.content) {
100
+ console.warn("Duplicate assistant message detected in Advanced Math section, skipping append.");
101
+
102
+ // Only trigger TTS for assistant messages if the last input was via voice
103
+ if (message.role === "assistant" && message.content !== "typing..." && mathLastInputWasVoice) {
104
+ mathSpeak(message.content);
105
+ }
106
+
107
+ return; // Exit to avoid appending the same message twice
108
+ }
109
+ }
110
+
111
+ const container = document.createElement("div");
112
+ container.classList.add("message-container");
113
+ const newMessage = document.createElement("div");
114
+ newMessage.classList.add("message");
115
+ newMessage.textContent = message.content;
116
+
117
+ if (message.role === "user") {
118
+ container.classList.add("user");
119
+ } else {
120
+ container.classList.add("assistant");
121
+ }
122
+
123
+ container.appendChild(newMessage);
124
+ mathChatBox.appendChild(container);
125
+ mathChatBox.scrollTop = mathChatBox.scrollHeight;
126
+
127
+ // Only trigger TTS for assistant messages if the last input was via voice
128
+ if (message.role === "assistant" && message.content !== "typing..." && mathLastInputWasVoice) {
129
+ mathSpeak(message.content);
130
+ }
131
+ }
132
+
133
+ function mathUpdateLastMessage(content) {
134
+ const messageDoms = document.getElementById("math-chat-box").querySelectorAll(".message");
135
+ const lastMessageDom = messageDoms[messageDoms.length - 1];
136
+ lastMessageDom.textContent = content;
137
+ }
138
+
139
+ function mathOnSpeechRecognized(transcript) {
140
+ const input = transcript.trim();
141
+ const message = {
142
+ content: input,
143
+ role: "user"
144
+ };
145
+ if (input.length === 0) {
146
+ return;
147
+ }
148
+ mathLastInputWasVoice = true; // Set flag as voice input
149
+ console.log(`Advanced Math Voice input received: ${input}`); // Debugging
150
+ document.getElementById("math-start_button").disabled = true;
151
+ document.getElementById("math-submit-button").disabled = true; // Disable submit button during processing
152
+
153
+ mathMessages.push(message);
154
+ mathAppendMessage(message);
155
+ logMessage(`User (Voice): ${input}`, "user");
156
+
157
+ // Append "typing..." placeholder
158
+ const aiPlaceholder = {
159
+ content: "typing...",
160
+ role: "assistant"
161
+ };
162
+ mathAppendMessage(aiPlaceholder);
163
+ logMessage("AdvancedMathBot is typing...", "system");
164
+
165
+ const onFinishGenerating = (finalMessage) => {
166
+ console.log(`Advanced Math Finishing generation with message: ${finalMessage}`); // Debugging
167
+ // Remove the "typing..." placeholder
168
+ const mathChatBox = document.getElementById("math-chat-box");
169
+ const lastMessageContainer = mathChatBox.lastElementChild;
170
+ if (lastMessageContainer && lastMessageContainer.querySelector(".message").textContent === "typing...") {
171
+ mathChatBox.removeChild(lastMessageContainer);
172
+ }
173
+
174
+ // Append the final message
175
+ const aiMessage = {
176
+ content: finalMessage,
177
+ role: "assistant"
178
+ };
179
+ mathAppendMessage(aiMessage);
180
+ logMessage(`AdvancedMathBot: ${finalMessage}`, "assistant");
181
+
182
+ document.getElementById("math-start_button").disabled = false;
183
+ document.getElementById("math-submit-button").disabled = false; // Re-enable submit button after processing
184
+ mathEngine.runtimeStatsText().then((statsText) => {
185
+ document.getElementById("math-chat-stats").classList.remove("hidden");
186
+ document.getElementById("math-chat-stats").textContent = statsText;
187
+ logMessage(`Runtime Stats: ${statsText}`, "system");
188
+ });
189
+ };
190
+
191
+ mathStreamingGenerating(
192
+ mathMessages,
193
+ mathUpdateLastMessage,
194
+ onFinishGenerating,
195
+ (err) => {
196
+ console.error(err);
197
+ alert("An error occurred while generating the response. Please try again.");
198
+ logMessage("Error during response generation.", "error");
199
+ document.getElementById("math-start_button").disabled = false;
200
+ document.getElementById("math-submit-button").disabled = false;
201
+ }
202
+ );
203
+ }
204
+
205
+ // Speech Recognition Code for Advanced Math
206
+ let mathRecognizing = false;
207
+ let mathIgnore_onend;
208
+ let mathFinal_transcript = '';
209
+ let mathRecognition;
210
+
211
+ function mathStartButton(event) {
212
+ if (mathRecognizing) {
213
+ mathRecognition.stop();
214
+ return;
215
+ }
216
+ mathFinal_transcript = '';
217
+ mathRecognition.lang = 'en-US';
218
+ mathRecognition.start();
219
+ mathIgnore_onend = false;
220
+ document.getElementById("math-start_button").classList.add("mic-animate");
221
+ logMessage("Voice input started.", "system");
222
+ }
223
+
224
+ if (!('webkitSpeechRecognition' in window)) {
225
+ alert("Web Speech API is not supported by this browser.");
226
+ logMessage("Web Speech API is not supported by this browser.", "error");
227
+ } else {
228
+ mathRecognition = new webkitSpeechRecognition();
229
+ mathRecognition.continuous = false; // Non-continuous recognition
230
+ mathRecognition.interimResults = false; // Get only final results
231
+
232
+ mathRecognition.onstart = function() {
233
+ mathRecognizing = true;
234
+ logMessage("Speech recognition started.", "system");
235
+ };
236
+
237
+ mathRecognition.onerror = function(event) {
238
+ if (event.error == 'no-speech') {
239
+ document.getElementById("math-start_button").classList.remove("mic-animate");
240
+ alert('No speech was detected in Advanced Mathematics section.');
241
+ logMessage("No speech detected.", "error");
242
+ mathIgnore_onend = true;
243
+ }
244
+ if (event.error == 'audio-capture') {
245
+ document.getElementById("math-start_button").classList.remove("mic-animate");
246
+ alert('No microphone was found in Advanced Mathematics section.');
247
+ logMessage("No microphone found.", "error");
248
+ mathIgnore_onend = true;
249
+ }
250
+ if (event.error == 'not-allowed') {
251
+ alert('Permission to use microphone was denied in Advanced Mathematics section.');
252
+ logMessage("Microphone permission denied.", "error");
253
+ mathIgnore_onend = true;
254
+ }
255
+ };
256
+
257
+ mathRecognition.onend = function() {
258
+ mathRecognizing = false;
259
+ document.getElementById("math-start_button").classList.remove("mic-animate");
260
+ logMessage("Speech recognition ended.", "system");
261
+ if (mathIgnore_onend) {
262
+ return;
263
+ }
264
+ if (!mathFinal_transcript) {
265
+ logMessage("No transcript captured.", "error");
266
+ return;
267
+ }
268
+ // Process the final transcript
269
+ mathOnSpeechRecognized(mathFinal_transcript);
270
+ };
271
+
272
+ mathRecognition.onresult = function(event) {
273
+ for (let i = event.resultIndex; i < event.results.length; ++i) {
274
+ if (event.results[i].isFinal) {
275
+ mathFinal_transcript += event.results[i][0].transcript;
276
+ }
277
+ }
278
+ mathFinal_transcript = mathFinal_transcript.trim();
279
+ logMessage(`Recognized Speech: ${mathFinal_transcript}`, "user");
280
+ };
281
+ }
282
+
283
+ document.getElementById("math-start_button").addEventListener("click", function(event) {
284
+ mathStartButton(event);
285
+ });
286
+
287
+ // Initialize Model Selection
288
+ mathAvailableModels.forEach((modelId) => {
289
+ const option = document.createElement("option");
290
+ option.value = modelId;
291
+ option.textContent = modelId;
292
+ document.getElementById("math-model-selection").appendChild(option);
293
+ });
294
+ document.getElementById("math-model-selection").value = mathSelectedModel;
295
+
296
+ // **Enable the Download Model button after models are loaded**
297
+ document.getElementById("math-download").disabled = false;
298
+
299
+ document.getElementById("math-download").addEventListener("click", function () {
300
+ mathInitializeWebLLMEngine().then(() => {
301
+ document.getElementById("math-start_button").disabled = false;
302
+ // Enable speech controls after model initialization
303
+ document.getElementById("math-speech-rate").disabled = false;
304
+ document.getElementById("math-speech-pitch").disabled = false;
305
+ logMessage("Model download initiated.", "system");
306
+ });
307
+ });
308
+
309
+ document.getElementById("math-clear-logs").addEventListener("click", function () {
310
+ document.getElementById("math-logs").innerHTML = '';
311
+ logMessage("Logs cleared.", "system");
312
+ });
313
+
314
+ // ===== TTS Integration =====
315
+
316
+ // Initialize Speech Synthesis
317
+ let mathSpeech = new SpeechSynthesisUtterance();
318
+ mathSpeech.lang = "en";
319
+
320
+ let mathVoices = [];
321
+
322
+ // Use addEventListener instead of directly assigning to onvoiceschanged
323
+ window.speechSynthesis.addEventListener("voiceschanged", () => {
324
+ mathVoices = window.speechSynthesis.getVoices();
325
+ mathPopulateVoices();
326
+ });
327
+
328
+ function mathPopulateVoices() {
329
+ const voiceSelect = document.getElementById("math-tools");
330
+ voiceSelect.innerHTML = ''; // Clear existing options
331
+ mathVoices.forEach((voice, i) => {
332
+ const option = new Option(voice.name, i);
333
+ voiceSelect.appendChild(option);
334
+ });
335
+ if (mathVoices.length > 0) {
336
+ const savedVoice = localStorage.getItem("mathSelectedVoice");
337
+ if (savedVoice !== null && mathVoices[savedVoice]) {
338
+ mathSpeech.voice = mathVoices[savedVoice];
339
+ voiceSelect.value = savedVoice;
340
+ } else {
341
+ mathSpeech.voice = mathVoices[0];
342
+ }
343
+ }
344
+ }
345
+
346
+ // Voice Selection Event Listener
347
+ document.getElementById("math-tools").addEventListener("change", () => {
348
+ const selectedVoiceIndex = document.getElementById("math-tools").value;
349
+ mathSpeech.voice = mathVoices[selectedVoiceIndex];
350
+ // Save to localStorage
351
+ localStorage.setItem("mathSelectedVoice", selectedVoiceIndex);
352
+ logMessage(`Voice changed to: ${mathVoices[selectedVoiceIndex].name}`, "system");
353
+ });
354
+
355
+ // Function to Speak Text with Voice Selection and Handling Large Texts
356
+ function mathSpeak(text) {
357
+ if (!window.speechSynthesis) {
358
+ console.warn("Speech Synthesis not supported in this browser for Advanced Mathematics section.");
359
+ logMessage("Speech Synthesis not supported in this browser.", "error");
360
+ return;
361
+ }
362
+
363
+ // Show spinner and enable Stop button
364
+ document.getElementById("math-loading-spinner").classList.remove("hidden");
365
+ document.getElementById("math-stop_button").disabled = false;
366
+ logMessage("TTS started.", "system");
367
+
368
+ // Retrieve the currently selected voice
369
+ const selectedVoice = mathSpeech.voice;
370
+
371
+ // Split the text into sentences to manage large texts
372
+ const sentences = text.match(/[^\.!\?]+[\.!\?]+/g) || [text];
373
+
374
+ let utterancesCount = sentences.length;
375
+
376
+ sentences.forEach(sentence => {
377
+ const utterance = new SpeechSynthesisUtterance(sentence.trim());
378
+
379
+ // Assign the selected voice to the utterance
380
+ if (selectedVoice) {
381
+ utterance.voice = selectedVoice;
382
+ }
383
+
384
+ // Assign rate and pitch from sliders
385
+ const rate = parseFloat(document.getElementById("math-speech-rate").value);
386
+ const pitch = parseFloat(document.getElementById("math-speech-pitch").value);
387
+ utterance.rate = rate; // Adjust the speaking rate (0.1 to 10)
388
+ utterance.pitch = pitch; // Adjust the pitch (0 to 2)
389
+
390
+ // Add event listeners for debugging or additional functionality
391
+ utterance.onstart = () => {
392
+ console.log("Speech started:", sentence);
393
+ logMessage(`TTS started: ${sentence.trim()}`, "system");
394
+ };
395
+
396
+ utterance.onend = () => {
397
+ console.log("Speech ended:", sentence);
398
+ logMessage(`TTS ended: ${sentence.trim()}`, "system");
399
+ utterancesCount--;
400
+ if (utterancesCount === 0) {
401
+ // Hide spinner and disable Stop button when all utterances have been spoken
402
+ document.getElementById("math-loading-spinner").classList.add("hidden");
403
+ document.getElementById("math-stop_button").disabled = true;
404
+ logMessage("All TTS messages have been spoken.", "system");
405
+ }
406
+ };
407
+
408
+ utterance.onerror = (e) => {
409
+ console.error("Speech Synthesis Error:", e);
410
+ alert("An error occurred during speech synthesis. Please try again.");
411
+ logMessage("Speech synthesis encountered an error.", "error");
412
+ utterancesCount = 0;
413
+ document.getElementById("math-loading-spinner").classList.add("hidden");
414
+ document.getElementById("math-stop_button").disabled = true;
415
+ };
416
+
417
+ window.speechSynthesis.speak(utterance);
418
+ });
419
+ }
420
+
421
+ // ===== New: Stop Speech Functionality =====
422
+
423
+ /**
424
+ * Stops any ongoing speech synthesis.
425
+ */
426
+ function mathStopSpeech() {
427
+ if (window.speechSynthesis.speaking) {
428
+ window.speechSynthesis.cancel();
429
+ document.getElementById("math-loading-spinner").classList.add("hidden");
430
+ document.getElementById("math-stop_button").disabled = true;
431
+ logMessage("Speech synthesis stopped by user.", "system");
432
+ }
433
+ }
434
+
435
+ // Event Listener for Stop Button
436
+ document.getElementById("math-stop_button").addEventListener("click", function () {
437
+ mathStopSpeech();
438
+ });
439
+
440
+ // ===== New: Text Input Handling =====
441
+
442
+ // Function to Handle Text Submission
443
+ function mathHandleTextSubmit() {
444
+ const textInput = document.getElementById("math-text-input");
445
+ const input = textInput.value.trim();
446
+ if (input.length === 0) {
447
+ return;
448
+ }
449
+ textInput.value = ''; // Clear the input field
450
+ const message = {
451
+ content: input,
452
+ role: "user" // Ensure this is correctly set
453
+ };
454
+ console.log(`Advanced Math Text input received: ${input}`); // Debugging
455
+ logMessage(`User: ${input}`, "user");
456
+
457
+ mathLastInputWasVoice = false; // Set flag as text input
458
+ document.getElementById("math-submit-button").disabled = true; // Disable to prevent multiple submissions
459
+
460
+ mathMessages.push(message);
461
+ mathAppendMessage(message);
462
+
463
+ // Append "typing..." placeholder
464
+ const aiPlaceholder = {
465
+ content: "typing...",
466
+ role: "assistant"
467
+ };
468
+ mathAppendMessage(aiPlaceholder);
469
+ logMessage("AdvancedMathBot is typing...", "system");
470
+
471
+ const onFinishGenerating = (finalMessage) => {
472
+ console.log(`Advanced Math Finishing generation with message: ${finalMessage}`); // Debugging
473
+ // Remove the "typing..." placeholder
474
+ const mathChatBox = document.getElementById("math-chat-box");
475
+ const lastMessageContainer = mathChatBox.lastElementChild;
476
+ if (lastMessageContainer && lastMessageContainer.querySelector(".message").textContent === "typing...") {
477
+ mathChatBox.removeChild(lastMessageContainer);
478
+ }
479
+
480
+ // Append the final message
481
+ const aiMessage = {
482
+ content: finalMessage,
483
+ role: "assistant"
484
+ };
485
+ mathAppendMessage(aiMessage);
486
+ logMessage(`AdvancedMathBot: ${finalMessage}`, "assistant");
487
+
488
+ // Trigger TTS for assistant messages if required
489
+ if (mathLastInputWasVoice) {
490
+ mathSpeak(finalMessage);
491
+ }
492
+
493
+ document.getElementById("math-submit-button").disabled = false; // Re-enable submit button after processing
494
+ mathEngine.runtimeStatsText().then((statsText) => {
495
+ document.getElementById("math-chat-stats").classList.remove("hidden");
496
+ document.getElementById("math-chat-stats").textContent = statsText;
497
+ logMessage(`Runtime Stats: ${statsText}`, "system");
498
+ });
499
+ };
500
+
501
+ mathStreamingGenerating(
502
+ mathMessages,
503
+ mathUpdateLastMessage,
504
+ onFinishGenerating,
505
+ (err) => {
506
+ console.error(err);
507
+ alert("An error occurred while generating the response. Please try again.");
508
+ logMessage("Error during response generation.", "error");
509
+ document.getElementById("math-submit-button").disabled = false;
510
+ }
511
+ );
512
+ }
513
+
514
+ // Event Listener for Submit Button
515
+ document.getElementById("math-submit-button").addEventListener("click", function () {
516
+ mathHandleTextSubmit();
517
+ });
518
+
519
+ // Event Listener for Enter Key in Text Input
520
+ document.getElementById("math-text-input").addEventListener("keypress", function (e) {
521
+ if (e.key === 'Enter') {
522
+ mathHandleTextSubmit();
523
+ }
524
+ });
525
+
526
+ // ===== Persisting User Preferences =====
527
+
528
+ // Load Preferences on Initialization
529
+ window.addEventListener("load", () => {
530
+ const savedVoice = localStorage.getItem("mathSelectedVoice");
531
+ if (savedVoice !== null && mathVoices[savedVoice]) {
532
+ document.getElementById("math-tools").value = savedVoice;
533
+ mathSpeech.voice = mathVoices[savedVoice];
534
+ logMessage(`Loaded saved voice: ${mathVoices[savedVoice].name}`, "system");
535
+ }
536
+
537
+ const savedRate = localStorage.getItem("mathSpeechRate");
538
+ if (savedRate !== null) {
539
+ document.getElementById("math-speech-rate").value = savedRate;
540
+ mathSpeech.rate = parseFloat(savedRate);
541
+ logMessage(`Loaded saved speech rate: ${savedRate}`, "system");
542
+ }
543
+
544
+ const savedPitch = localStorage.getItem("mathSpeechPitch");
545
+ if (savedPitch !== null) {
546
+ document.getElementById("math-speech-pitch").value = savedPitch;
547
+ mathSpeech.pitch = parseFloat(savedPitch);
548
+ logMessage(`Loaded saved speech pitch: ${savedPitch}`, "system");
549
+ }
550
+ });
551
+
552
+ // Save Speech Rate
553
+ document.getElementById("math-speech-rate").addEventListener("input", (e) => {
554
+ const rate = e.target.value;
555
+ mathSpeech.rate = parseFloat(rate);
556
+ localStorage.setItem("mathSpeechRate", rate);
557
+ logMessage(`Speech rate changed to: ${rate}`, "system");
558
+ });
559
+
560
+ // Save Speech Pitch
561
+ document.getElementById("math-speech-pitch").addEventListener("input", (e) => {
562
+ const pitch = e.target.value;
563
+ mathSpeech.pitch = parseFloat(pitch);
564
+ localStorage.setItem("mathSpeechPitch", pitch);
565
+ logMessage(`Speech pitch changed to: ${pitch}`, "system");
566
+ });
567
+
568
+ // ===== Logging Function =====
569
+
570
+ /**
571
+ * Logs messages to the #math-logs container.
572
+ * @param {string} message - The message to log.
573
+ * @param {string} type - The type of message: 'user', 'assistant', 'system', 'error'.
574
+ */
575
+ function logMessage(message, type) {
576
+ const mathLogs = document.getElementById("math-logs");
577
+ const logEntry = document.createElement("div");
578
+ logEntry.classList.add("log-entry");
579
+ logEntry.textContent = `[${type.toUpperCase()}] ${message}`;
580
+
581
+ // Style log entries based on type
582
+ switch(type) {
583
+ case 'user':
584
+ logEntry.style.color = "#00796B";
585
+ break;
586
+ case 'assistant':
587
+ logEntry.style.color = "#004D40";
588
+ break;
589
+ case 'system':
590
+ logEntry.style.color = "#555555";
591
+ break;
592
+ case 'error':
593
+ logEntry.style.color = "#E53935";
594
+ break;
595
+ default:
596
+ logEntry.style.color = "#000000";
597
+ }
598
+
599
+ mathLogs.appendChild(logEntry);
600
+ mathLogs.scrollTop = mathLogs.scrollHeight;
601
+ }
602
+
603
+ // ===== TTS Integration Continued =====
604
+
605
+ // Optional: Global Listener to Detect When All Speech Has Finished
606
+ window.speechSynthesis.addEventListener('end', () => {
607
+ console.log("All advanced math speech has been spoken.");
608
+ logMessage("All TTS messages have been spoken.", "system");
609
+ // Ensure Stop button is disabled after speech ends
610
+ document.getElementById("math-stop_button").disabled = true;
611
+ });
612
+ });
base.css ADDED
@@ -0,0 +1,242 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Reset and base styles */
2
+ * {
3
+ box-sizing: border-box;
4
+ margin: 0;
5
+ padding: 0;
6
+ }
7
+
8
+
9
+ body {
10
+ font-family: 'Inter', sans-serif;
11
+ background: linear-gradient(135deg, #f0f4f8, #d9e2ec);
12
+ display: flex;
13
+ min-height: 100vh;
14
+ color: #333;
15
+ overflow: hidden;
16
+ }
17
+
18
+ /* Sidebar styling */
19
+ .sidebar {
20
+ width: 260px;
21
+ background: linear-gradient(135deg, #ffffff, #e0f7fa);
22
+ box-shadow: 2px 0 10px rgba(0,0,0,0.1);
23
+ flex-shrink: 0;
24
+ display: flex;
25
+ flex-direction: column;
26
+ padding: 30px 20px;
27
+ position: fixed;
28
+ height: 100%;
29
+ overflow-y: auto;
30
+ transition: width 0.3s ease, background 0.3s ease;
31
+ }
32
+
33
+ .sidebar h2 {
34
+ text-align: center;
35
+ margin-bottom: 40px;
36
+ font-family: 'Roboto', sans-serif;
37
+ font-weight: 700;
38
+ font-size: 1.8em;
39
+ color: #00796B;
40
+ background: linear-gradient(90deg, #00796B, #004D40);
41
+ -webkit-background-clip: text;
42
+ -webkit-text-fill-color: transparent;
43
+ }
44
+
45
+ .sidebar a {
46
+ color: #555555;
47
+ padding: 12px 15px;
48
+ text-decoration: none;
49
+ font-size: 1em;
50
+ border-radius: 8px;
51
+ margin-bottom: 10px;
52
+ transition: background-color 0.3s, color 0.3s, transform 0.2s, box-shadow 0.3s;
53
+ display: flex;
54
+ align-items: center;
55
+ background: linear-gradient(135deg, #ffffff, #f9f9f9);
56
+ }
57
+
58
+ .sidebar a:hover {
59
+ background: linear-gradient(135deg, #e0f7fa, #ffffff);
60
+ color: #00796B;
61
+ transform: translateX(5px);
62
+ box-shadow: 0 4px 12px rgba(0,0,0,0.05);
63
+ }
64
+
65
+ .sidebar a.active {
66
+ background: linear-gradient(135deg, #80deea, #4dd0e1);
67
+ color: #004d40;
68
+ font-weight: 600;
69
+ box-shadow: 0 4px 12px rgba(0,0,0,0.1);
70
+ }
71
+
72
+ .sidebar a::before {
73
+ content: '';
74
+ display: inline-block;
75
+ width: 8px;
76
+ height: 8px;
77
+ background-color: #004d40;
78
+ border-radius: 50%;
79
+ margin-right: 12px;
80
+ opacity: 0;
81
+ transition: opacity 0.3s;
82
+ }
83
+
84
+ .sidebar a.active::before {
85
+ opacity: 1;
86
+ }
87
+
88
+ /* Main content styling */
89
+ .main-content {
90
+ margin-left: 260px;
91
+ padding: 30px;
92
+ flex: 1;
93
+ background: linear-gradient(135deg, #f9f9f9, #e0f2f1);
94
+ overflow-y: auto;
95
+ height: 100vh;
96
+ transition: margin-left 0.3s ease, background 0.3s ease;
97
+ }
98
+
99
+ /* Header */
100
+ .header {
101
+ display: flex;
102
+ justify-content: space-between;
103
+ align-items: center;
104
+ margin-bottom: 25px;
105
+ background: linear-gradient(90deg, #80deea, #4dd0e1);
106
+ padding: 15px 20px;
107
+ border-radius: 12px;
108
+ box-shadow: 0 4px 10px rgba(0,0,0,0.05);
109
+ }
110
+
111
+ .header h1 {
112
+ font-family: 'Roboto', sans-serif;
113
+ font-size: 2em;
114
+ color: #ffffff;
115
+ background: linear-gradient(90deg, #ffffff, #e0f7fa);
116
+ -webkit-background-clip: text;
117
+ -webkit-text-fill-color: transparent;
118
+ }
119
+
120
+
121
+
122
+ /* Hidden Class */
123
+ .hidden {
124
+ display: none;
125
+ }
126
+
127
+ /* Scrollbar Styling */
128
+ #chat-box::-webkit-scrollbar,
129
+ #logs::-webkit-scrollbar {
130
+ width: 8px;
131
+ }
132
+
133
+
134
+ /* Responsive Adjustments */
135
+ @media (max-width: 768px) {
136
+ .sidebar {
137
+ width: 220px;
138
+ }
139
+
140
+ .main-content {
141
+ margin-left: 220px;
142
+ }
143
+ }
144
+
145
+ @media (max-width: 600px) {
146
+ .sidebar {
147
+ position: relative;
148
+ width: 100%;
149
+ height: auto;
150
+ flex-direction: row;
151
+ overflow-x: auto;
152
+ padding: 15px 10px;
153
+ }
154
+
155
+ .sidebar h2 {
156
+ display: none;
157
+ }
158
+
159
+ .sidebar a {
160
+ margin-right: 10px;
161
+ margin-bottom: 0;
162
+ padding: 10px 12px;
163
+ font-size: 0.9em;
164
+ }
165
+
166
+ .main-content {
167
+ margin-left: 0;
168
+ padding: 20px 15px;
169
+ }
170
+
171
+ .header {
172
+ flex-direction: column;
173
+ align-items: flex-start;
174
+ }
175
+
176
+ .header h1 {
177
+ margin-bottom: 10px;
178
+ font-size: 1.5em;
179
+ }
180
+ }
181
+
182
+
183
+ /* Configuration Info */
184
+ #configuration {
185
+ margin-top: 15px;
186
+ font-size: 0.9em;
187
+ color: #555555;
188
+ }
189
+
190
+
191
+ /* Controls Container Styling */
192
+ #controls {
193
+ display: flex;
194
+ gap: 10px;
195
+ margin-bottom: 15px;
196
+ flex-wrap: wrap;
197
+ }
198
+
199
+
200
+
201
+ /* Button Styling */
202
+ button {
203
+ background: linear-gradient(135deg, #00796B, #004D40);
204
+ border: none;
205
+ color: white;
206
+ padding: 12px 20px;
207
+ font-size: 1em;
208
+ border-radius: 8px;
209
+ cursor: pointer;
210
+ transition: background 0.3s, transform 0.2s, box-shadow 0.3s;
211
+ box-shadow: 0 2px 8px rgba(0,0,0,0.1);
212
+ }
213
+
214
+ button:hover {
215
+ background: linear-gradient(135deg, #005D56, #00332E);
216
+ transform: translateY(-2px);
217
+ box-shadow: 0 4px 12px rgba(0,0,0,0.2);
218
+ }
219
+
220
+ button:disabled {
221
+ background: linear-gradient(135deg, #A5D6A7, #81C784);
222
+ cursor: not-allowed;
223
+ box-shadow: none;
224
+ }
225
+
226
+
227
+ /* Card Styling */
228
+ .card {
229
+ background: linear-gradient(135deg, #ffffff, #f1f8e9);
230
+ border-radius: 16px;
231
+ box-shadow: 0 4px 20px rgba(0,0,0,0.05);
232
+ padding: 25px;
233
+ margin-bottom: 25px;
234
+ transition: transform 0.3s, box-shadow 0.3s, background 0.3s;
235
+ }
236
+
237
+ .card:hover {
238
+ transform: translateY(-5px);
239
+ box-shadow: 0 6px 25px rgba(0,0,0,0.1);
240
+ background: linear-gradient(135deg, #e0f7fa, #ffffff);
241
+ }
242
+
coder.css ADDED
@@ -0,0 +1,422 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* coder.css */
2
+
3
+ /* Reset and Base Styles */
4
+ #coder * {
5
+ box-sizing: border-box;
6
+ font-family: Arial, sans-serif;
7
+ }
8
+
9
+ /* Card Styling */
10
+ .coder-card {
11
+ display: flex;
12
+ gap: 20px;
13
+ background: linear-gradient(135deg, #ffffff, #f1f8e9);
14
+ border-radius: 16px;
15
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05);
16
+ padding: 25px;
17
+ margin: 25px auto;
18
+
19
+ transition: transform 0.3s, box-shadow 0.3s, background 0.3s;
20
+ }
21
+
22
+ .coder-card:hover {
23
+ transform: translateY(-5px);
24
+ box-shadow: 0 6px 25px rgba(0, 0, 0, 0.1);
25
+ background: linear-gradient(135deg, #e0f7fa, #ffffff);
26
+ }
27
+
28
+ /* Flex Layout for Coder Options and Chat Window */
29
+ .coder-options {
30
+ flex: 1;
31
+ display: flex;
32
+ flex-direction: column;
33
+ gap: 20px;
34
+ }
35
+
36
+ /* Ensure Coder Options takes up available vertical space */
37
+ .coder-options {
38
+ height: 100%;
39
+ }
40
+
41
+ /* Chat Window Styling */
42
+ .chat-window {
43
+ flex: 2;
44
+ display: flex;
45
+ flex-direction: column;
46
+ gap: 20px;
47
+ }
48
+
49
+ /* Option Sections Styling */
50
+ .option-section {
51
+ background-color: #ffffff;
52
+ padding: 15px;
53
+ border-radius: 12px;
54
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
55
+ transition: background 0.3s, box-shadow 0.3s;
56
+ }
57
+
58
+ .option-section:hover {
59
+ background-color: #f9f9f9;
60
+ }
61
+
62
+ /* Button Styling (Reused from Original) */
63
+ #coder button {
64
+ background: linear-gradient(135deg, #00796B, #004D40);
65
+ border: none;
66
+ color: white;
67
+ padding: 12px 20px;
68
+ font-size: 1em;
69
+ border-radius: 8px;
70
+ cursor: pointer;
71
+ transition: background 0.3s, transform 0.2s, box-shadow 0.3s;
72
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
73
+ }
74
+
75
+ #coder button:hover:not(:disabled) {
76
+ background: linear-gradient(135deg, #005D56, #00332E);
77
+ transform: translateY(-2px);
78
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
79
+ }
80
+
81
+ #coder button:disabled {
82
+ background: linear-gradient(135deg, #A5D6A7, #81C784);
83
+ cursor: not-allowed;
84
+ box-shadow: none;
85
+ }
86
+
87
+ /* Select Dropdown Styling (Reused from Original) */
88
+ #coder select {
89
+ width: 100%;
90
+ padding: 10px 15px;
91
+ border: 1px solid #cccccc;
92
+ border-radius: 8px;
93
+ font-size: 1em;
94
+ background-color: #ffffff;
95
+ transition: border-color 0.3s, box-shadow 0.3s;
96
+ margin-bottom: 15px;
97
+ }
98
+
99
+ #coder select:focus {
100
+ border-color: #00796B;
101
+ box-shadow: 0 0 5px rgba(0, 121, 107, 0.5);
102
+ outline: none;
103
+ }
104
+
105
+ /* Speech Controls Styling */
106
+ #coder-speech-controls label {
107
+ display: block;
108
+ margin-bottom: 5px;
109
+ color: #555555;
110
+ font-weight: 500;
111
+ }
112
+
113
+ #coder-speech-controls input[type="range"] {
114
+ width: 100%;
115
+ margin-bottom: 15px;
116
+ }
117
+
118
+ /* Logs Container Styling */
119
+ #coder-logs-container {
120
+ display: flex;
121
+ flex-direction: column;
122
+ gap: 10px;
123
+ max-height: 200px; /* Set a maximum height for the logs container */
124
+ overflow: hidden; /* Hide any overflow to prevent the container from expanding */
125
+ }
126
+
127
+ #coder-logs {
128
+ flex: 1;
129
+ max-height: 200px; /* Adjust as needed */
130
+ border: 1px solid #e0e0e0;
131
+ border-radius: 12px;
132
+ overflow-y: auto; /* Enable vertical scrolling */
133
+ padding: 10px;
134
+ background-color: #f9f9f9;
135
+ font-size: 0.85em;
136
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.05);
137
+ }
138
+
139
+ #coder-clear-logs {
140
+ align-self: flex-end;
141
+ background: linear-gradient(135deg, #FF5252, #E53935);
142
+ border: none;
143
+ color: white;
144
+ padding: 8px 16px;
145
+ font-size: 0.9em;
146
+ cursor: pointer;
147
+ border-radius: 8px;
148
+ transition: background 0.3s, transform 0.2s, box-shadow 0.3s;
149
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
150
+ }
151
+
152
+ #coder-clear-logs:hover {
153
+ background: linear-gradient(135deg, #E53935, #D32F2F);
154
+ transform: scale(1.02);
155
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
156
+ }
157
+
158
+ /* Chat Box Styling */
159
+ #coder-chat-box {
160
+ height: 500px;
161
+ border: 1px solid #e0e0e0;
162
+ border-radius: 12px;
163
+ overflow-y: auto;
164
+ padding: 20px;
165
+ background-color: #ffffff;
166
+ position: relative;
167
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.05);
168
+ margin-bottom: 20px;
169
+ }
170
+
171
+ /* Message Styling */
172
+ #coder .message-container {
173
+ margin-bottom: 20px;
174
+ display: flex;
175
+ }
176
+
177
+ #coder .message {
178
+ padding: 12px 18px;
179
+ border-radius: 20px;
180
+ max-width: 75%;
181
+ position: relative;
182
+ word-wrap: break-word;
183
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
184
+ font-size: 0.95em;
185
+ line-height: 1.4;
186
+ background-color: #f1f8e9;
187
+ transition: background-color 0.3s, box-shadow 0.3s;
188
+ }
189
+
190
+ #coder .user .message {
191
+ background: #DCF8C6;
192
+ border-bottom-right-radius: 0;
193
+ }
194
+
195
+ #coder .assistant .message {
196
+ background: #e3f2fd;
197
+ border-bottom-left-radius: 0;
198
+ }
199
+
200
+ /* Chat Input Container Styling */
201
+ #coder-text-input-container {
202
+ display: flex;
203
+ gap: 10px;
204
+ }
205
+
206
+ #coder-text-input {
207
+ flex: 1;
208
+ padding: 10px 15px;
209
+ border: 1px solid #cccccc;
210
+ border-radius: 8px;
211
+ font-size: 1em;
212
+ background-color: #ffffff;
213
+ transition: border-color 0.3s, box-shadow 0.3s;
214
+ }
215
+
216
+ #coder-text-input:focus {
217
+ border-color: #00796B;
218
+ box-shadow: 0 0 5px rgba(0, 121, 107, 0.5);
219
+ outline: none;
220
+ }
221
+
222
+ #coder-submit-button {
223
+ background: linear-gradient(135deg, #00796B, #004D40);
224
+ border: none;
225
+ color: white;
226
+ padding: 10px 20px;
227
+ font-size: 1em;
228
+ border-radius: 8px;
229
+ cursor: pointer;
230
+ transition: background 0.3s, transform 0.2s, box-shadow 0.3s;
231
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
232
+ }
233
+
234
+ #coder-submit-button:hover:not(:disabled) {
235
+ background: linear-gradient(135deg, #005D56, #00332E);
236
+ transform: translateY(-2px);
237
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
238
+ }
239
+
240
+ #coder-submit-button:disabled {
241
+ background: linear-gradient(135deg, #A5D6A7, #81C784);
242
+ cursor: not-allowed;
243
+ box-shadow: none;
244
+ }
245
+
246
+ /* Microphone and Stop Buttons Styling */
247
+ #coder-mic-container {
248
+ display: flex;
249
+ justify-content: center;
250
+ gap: 15px;
251
+ margin-top: 20px;
252
+ }
253
+
254
+ #coder-start_button,
255
+ #coder-stop_button {
256
+ background: linear-gradient(135deg, #00796B, #004D40);
257
+ border: none;
258
+ color: white;
259
+ padding: 15px;
260
+ border-radius: 50%;
261
+ cursor: pointer;
262
+ transition: background 0.3s, transform 0.2s, box-shadow 0.3s;
263
+ width: 60px;
264
+ height: 60px;
265
+ display: flex;
266
+ justify-content: center;
267
+ align-items: center;
268
+ position: relative;
269
+ overflow: hidden;
270
+ box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
271
+ }
272
+
273
+ #coder-stop_button {
274
+ background: linear-gradient(135deg, #E53935, #D32F2F);
275
+ }
276
+
277
+ #coder-start_button::before,
278
+ #coder-stop_button::before {
279
+ content: '';
280
+ position: absolute;
281
+ width: 200%;
282
+ height: 200%;
283
+ background: rgba(255, 255, 255, 0.3);
284
+ top: 50%;
285
+ left: 50%;
286
+ transform: translate(-50%, -50%) scale(0);
287
+ border-radius: 50%;
288
+ transition: transform 0.5s ease-out;
289
+ }
290
+
291
+ #coder-start_button:active::before,
292
+ #coder-stop_button:active::before {
293
+ transform: translate(-50%, -50%) scale(1);
294
+ }
295
+
296
+ #coder-start_button:disabled,
297
+ #coder-stop_button:disabled {
298
+ background: linear-gradient(135deg, #A5D6A7, #81C784);
299
+ cursor: not-allowed;
300
+ box-shadow: none;
301
+ }
302
+
303
+ #coder-start_button:hover:not(:disabled),
304
+ #coder-stop_button:hover:not(:disabled) {
305
+ transform: scale(1.05);
306
+ box-shadow: 0 6px 15px rgba(0, 0, 0, 0.15);
307
+ }
308
+
309
+ #coder-stop_button:hover:not(:disabled) {
310
+ background: linear-gradient(135deg, #D32F2F, #C62828);
311
+ }
312
+
313
+ /* Icon Styling */
314
+ #coder .mic-icon,
315
+ #coder .stop-icon {
316
+ width: 30px;
317
+ height: 30px;
318
+ transition: transform 0.3s;
319
+ fill: #ffffff;
320
+ }
321
+
322
+ /* Animation for Mic Icon */
323
+ #coder .mic-animate {
324
+ animation: pulse 2s infinite;
325
+ }
326
+
327
+ @keyframes pulse {
328
+ 0% {
329
+ transform: scale(1);
330
+ }
331
+ 50% {
332
+ transform: scale(1.2);
333
+ }
334
+ 100% {
335
+ transform: scale(1);
336
+ }
337
+ }
338
+
339
+ /* Chat Stats */
340
+ #coder-chat-stats {
341
+ font-size: 0.85em;
342
+ color: #555555;
343
+ text-align: center;
344
+ margin-top: 10px;
345
+ }
346
+
347
+ /* Hidden Class */
348
+ #coder .hidden {
349
+ display: none;
350
+ }
351
+
352
+ /* Scrollbar Styling */
353
+ #coder-chat-box::-webkit-scrollbar,
354
+ #coder-logs::-webkit-scrollbar {
355
+ width: 8px;
356
+ }
357
+
358
+ #coder-chat-box::-webkit-scrollbar-track,
359
+ #coder-logs::-webkit-scrollbar-track {
360
+ background: #f1f1f1;
361
+ border-radius: 4px;
362
+ }
363
+
364
+ #coder-chat-box::-webkit-scrollbar-thumb,
365
+ #coder-logs::-webkit-scrollbar-thumb {
366
+ background: #cccccc;
367
+ border-radius: 4px;
368
+ }
369
+
370
+ #coder-chat-box::-webkit-scrollbar-thumb:hover,
371
+ #coder-logs::-webkit-scrollbar-thumb:hover {
372
+ background: #a8a8a8;
373
+ }
374
+
375
+ /* Initialize Button Styling (Reused from Original) */
376
+ #coder-download {
377
+ flex: 0 0 auto;
378
+ background: linear-gradient(135deg, #00796B, #004D40);
379
+ border: none;
380
+ color: white;
381
+ padding: 10px 20px;
382
+ font-size: 1em;
383
+ border-radius: 8px;
384
+ cursor: pointer;
385
+ transition: background 0.3s, transform 0.2s, box-shadow 0.3s;
386
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
387
+ }
388
+
389
+ #coder-download:hover:not(:disabled) {
390
+ background: linear-gradient(135deg, #005D56, #00332E);
391
+ transform: translateY(-2px);
392
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
393
+ }
394
+
395
+ #coder-download:disabled {
396
+ background: linear-gradient(135deg, #A5D6A7, #81C784);
397
+ cursor: not-allowed;
398
+ box-shadow: none;
399
+ }
400
+
401
+ /* Configuration Info Styling */
402
+ #coder-configuration {
403
+ margin-top: 15px;
404
+ font-size: 0.9em;
405
+ color: #555555;
406
+ }
407
+
408
+ /* Responsive Grid Adjustments */
409
+ @media (max-width: 800px) {
410
+ .coder-card {
411
+ flex-direction: column;
412
+ }
413
+
414
+ /* Adjust Logs Container Height for Smaller Screens */
415
+ #coder-logs-container {
416
+ max-height: 200px;
417
+ }
418
+
419
+ #coder-logs {
420
+ max-height: 150px;
421
+ }
422
+ }
coder.js ADDED
@@ -0,0 +1,612 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // coder.js
2
+ import * as webllm from "https://esm.run/@mlc-ai/web-llm";
3
+
4
+ // Ensure the script runs after the DOM is fully loaded
5
+ document.addEventListener("DOMContentLoaded", () => {
6
+ // Initialize the Young Coder section
7
+ const coderMessages = [
8
+ {
9
+ content: "You are Aged Guru, an intelligent assistant skilled in coding and software development. Provide insightful and comprehensive answers to complex programming questions.",
10
+ role: "system"
11
+ }
12
+ ];
13
+
14
+ const coderAvailableModels = webllm.prebuiltAppConfig.model_list.map(
15
+ (m) => m.model_id
16
+ );
17
+ let coderSelectedModel = "Qwen2.5-Coder-1.5B-Instruct-q4f16_1-MLC"; // Default model
18
+
19
+ function coderUpdateEngineInitProgressCallback(report) {
20
+ console.log("Coder Initialize", report.progress);
21
+ // Instead of updating a status span, log the progress
22
+ logMessage(`Model Initialization Progress: ${report.text}`, "system");
23
+ }
24
+
25
+ const coderEngine = new webllm.MLCEngine();
26
+ coderEngine.setInitProgressCallback(coderUpdateEngineInitProgressCallback);
27
+
28
+ let coderIsGenerating = false; // Flag to prevent multiple generations
29
+
30
+ async function coderInitializeWebLLMEngine() {
31
+ logMessage("Model initialization started.", "system");
32
+ document.getElementById("coder-loading-spinner").classList.remove("hidden"); // Show spinner
33
+ coderSelectedModel = document.getElementById("coder-model-selection").value;
34
+ const config = {
35
+ temperature: 0.7, // Adjusted for more precise answers
36
+ top_p: 0.9
37
+ };
38
+ try {
39
+ await coderEngine.reload(coderSelectedModel, config);
40
+ document.getElementById("coder-selected-model").textContent = coderSelectedModel;
41
+ document.getElementById("coder-start_button").disabled = false;
42
+ document.getElementById("coder-text-input").disabled = false; // Enable text input after initialization
43
+ document.getElementById("coder-submit-button").disabled = false; // Enable submit button after initialization
44
+ document.getElementById("coder-speech-controls").disabled = false; // Enable speech controls after initialization
45
+ document.getElementById("coder-configuration").classList.remove("hidden");
46
+ logMessage("Model initialized successfully.", "system");
47
+ } catch (error) {
48
+ console.error("Error initializing the model:", error);
49
+ alert("Failed to initialize the model. Please try again.");
50
+ logMessage("Failed to initialize the model.", "error");
51
+ } finally {
52
+ document.getElementById("coder-loading-spinner").classList.add("hidden"); // Hide spinner
53
+ }
54
+ }
55
+
56
+ async function coderStreamingGenerating(messages, onUpdate, onFinish, onError) {
57
+ if (coderIsGenerating) {
58
+ console.warn("Coder Generation already in progress.");
59
+ return;
60
+ }
61
+ coderIsGenerating = true;
62
+ try {
63
+ let curMessage = "";
64
+ const completion = await coderEngine.chat.completions.create({
65
+ stream: true,
66
+ messages
67
+ });
68
+ for await (const chunk of completion) {
69
+ const curDelta = chunk.choices[0].delta.content;
70
+ if (curDelta) {
71
+ curMessage += curDelta;
72
+ }
73
+ onUpdate(curMessage);
74
+ }
75
+ const finalMessage = await coderEngine.getMessage();
76
+ console.log(`Coder Generated final message: ${finalMessage}`); // Debugging
77
+ onFinish(finalMessage);
78
+ logMessage("Response generated successfully.", "system");
79
+ } catch (err) {
80
+ console.error(err);
81
+ onError(err);
82
+ logMessage("An error occurred during response generation.", "error");
83
+ } finally {
84
+ coderIsGenerating = false;
85
+ }
86
+ }
87
+
88
+ // Flag to track the last input method
89
+ let coderLastInputWasVoice = false;
90
+
91
+ function coderAppendMessage(message) {
92
+ console.log(`Coder Appending message: ${message.content} (Role: ${message.role})`); // Debugging
93
+ const coderChatBox = document.getElementById("coder-chat-box");
94
+
95
+ // Check if the assistant's message is already appended to avoid duplication
96
+ if (message.role === "assistant") {
97
+ const existingMessages = coderChatBox.querySelectorAll(".message");
98
+ const lastMessage = existingMessages[existingMessages.length - 1];
99
+ if (lastMessage && lastMessage.textContent === message.content) {
100
+ console.warn("Duplicate assistant message detected in Coder section, skipping append.");
101
+
102
+ // Only trigger TTS for assistant messages if the last input was via voice
103
+ if (message.role === "assistant" && message.content !== "typing..." && coderLastInputWasVoice) {
104
+ coderSpeak(message.content);
105
+ }
106
+
107
+ return; // Exit to avoid appending the same message twice
108
+ }
109
+ }
110
+
111
+ const container = document.createElement("div");
112
+ container.classList.add("message-container");
113
+ const newMessage = document.createElement("div");
114
+ newMessage.classList.add("message");
115
+ newMessage.textContent = message.content;
116
+
117
+ if (message.role === "user") {
118
+ container.classList.add("user");
119
+ } else {
120
+ container.classList.add("assistant");
121
+ }
122
+
123
+ container.appendChild(newMessage);
124
+ coderChatBox.appendChild(container);
125
+ coderChatBox.scrollTop = coderChatBox.scrollHeight;
126
+
127
+ // Only trigger TTS for assistant messages if the last input was via voice
128
+ if (message.role === "assistant" && message.content !== "typing..." && coderLastInputWasVoice) {
129
+ coderSpeak(message.content);
130
+ }
131
+ }
132
+
133
+ function coderUpdateLastMessage(content) {
134
+ const messageDoms = document.getElementById("coder-chat-box").querySelectorAll(".message");
135
+ const lastMessageDom = messageDoms[messageDoms.length - 1];
136
+ lastMessageDom.textContent = content;
137
+ }
138
+
139
+ function coderOnSpeechRecognized(transcript) {
140
+ const input = transcript.trim();
141
+ const message = {
142
+ content: input,
143
+ role: "user"
144
+ };
145
+ if (input.length === 0) {
146
+ return;
147
+ }
148
+ coderLastInputWasVoice = true; // Set flag as voice input
149
+ console.log(`Coder Voice input received: ${input}`); // Debugging
150
+ document.getElementById("coder-start_button").disabled = true;
151
+ document.getElementById("coder-submit-button").disabled = true; // Disable submit button during processing
152
+
153
+ coderMessages.push(message);
154
+ coderAppendMessage(message);
155
+ logMessage(`User (Voice): ${input}`, "user");
156
+
157
+ // Append "typing..." placeholder
158
+ const aiPlaceholder = {
159
+ content: "typing...",
160
+ role: "assistant"
161
+ };
162
+ coderAppendMessage(aiPlaceholder);
163
+ logMessage("CoderBot is typing...", "system");
164
+
165
+ const onFinishGenerating = (finalMessage) => {
166
+ console.log(`Coder Finishing generation with message: ${finalMessage}`); // Debugging
167
+ // Remove the "typing..." placeholder
168
+ const coderChatBox = document.getElementById("coder-chat-box");
169
+ const lastMessageContainer = coderChatBox.lastElementChild;
170
+ if (lastMessageContainer && lastMessageContainer.querySelector(".message").textContent === "typing...") {
171
+ coderChatBox.removeChild(lastMessageContainer);
172
+ }
173
+
174
+ // Append the final message
175
+ const aiMessage = {
176
+ content: finalMessage,
177
+ role: "assistant"
178
+ };
179
+ coderAppendMessage(aiMessage);
180
+ logMessage(`CoderBot: ${finalMessage}`, "assistant");
181
+
182
+ document.getElementById("coder-start_button").disabled = false;
183
+ document.getElementById("coder-submit-button").disabled = false; // Re-enable submit button after processing
184
+ coderEngine.runtimeStatsText().then((statsText) => {
185
+ document.getElementById("coder-chat-stats").classList.remove("hidden");
186
+ document.getElementById("coder-chat-stats").textContent = statsText;
187
+ logMessage(`Runtime Stats: ${statsText}`, "system");
188
+ });
189
+ };
190
+
191
+ coderStreamingGenerating(
192
+ coderMessages,
193
+ coderUpdateLastMessage,
194
+ onFinishGenerating,
195
+ (err) => {
196
+ console.error(err);
197
+ alert("An error occurred while generating the response. Please try again.");
198
+ logMessage("Error during response generation.", "error");
199
+ document.getElementById("coder-start_button").disabled = false;
200
+ document.getElementById("coder-submit-button").disabled = false;
201
+ }
202
+ );
203
+ }
204
+
205
+ // Speech Recognition Code for Coder
206
+ let coderRecognizing = false;
207
+ let coderIgnore_onend;
208
+ let coderFinal_transcript = '';
209
+ let coderRecognition;
210
+
211
+ function coderStartButton(event) {
212
+ if (coderRecognizing) {
213
+ coderRecognition.stop();
214
+ return;
215
+ }
216
+ coderFinal_transcript = '';
217
+ coderRecognition.lang = 'en-US';
218
+ coderRecognition.start();
219
+ coderIgnore_onend = false;
220
+ document.getElementById("coder-start_button").classList.add("mic-animate");
221
+ logMessage("Voice input started.", "system");
222
+ }
223
+
224
+ if (!('webkitSpeechRecognition' in window)) {
225
+ alert("Web Speech API is not supported by this browser.");
226
+ logMessage("Web Speech API is not supported by this browser.", "error");
227
+ } else {
228
+ coderRecognition = new webkitSpeechRecognition();
229
+ coderRecognition.continuous = false; // Non-continuous recognition
230
+ coderRecognition.interimResults = false; // Get only final results
231
+
232
+ coderRecognition.onstart = function() {
233
+ coderRecognizing = true;
234
+ logMessage("Speech recognition started.", "system");
235
+ };
236
+
237
+ coderRecognition.onerror = function(event) {
238
+ if (event.error == 'no-speech') {
239
+ document.getElementById("coder-start_button").classList.remove("mic-animate");
240
+ alert('No speech was detected in Coder section.');
241
+ logMessage("No speech detected.", "error");
242
+ coderIgnore_onend = true;
243
+ }
244
+ if (event.error == 'audio-capture') {
245
+ document.getElementById("coder-start_button").classList.remove("mic-animate");
246
+ alert('No microphone was found in Coder section.');
247
+ logMessage("No microphone found.", "error");
248
+ coderIgnore_onend = true;
249
+ }
250
+ if (event.error == 'not-allowed') {
251
+ alert('Permission to use microphone was denied in Coder section.');
252
+ logMessage("Microphone permission denied.", "error");
253
+ coderIgnore_onend = true;
254
+ }
255
+ };
256
+
257
+ coderRecognition.onend = function() {
258
+ coderRecognizing = false;
259
+ document.getElementById("coder-start_button").classList.remove("mic-animate");
260
+ logMessage("Speech recognition ended.", "system");
261
+ if (coderIgnore_onend) {
262
+ return;
263
+ }
264
+ if (!coderFinal_transcript) {
265
+ logMessage("No transcript captured.", "error");
266
+ return;
267
+ }
268
+ // Process the final transcript
269
+ coderOnSpeechRecognized(coderFinal_transcript);
270
+ };
271
+
272
+ coderRecognition.onresult = function(event) {
273
+ for (let i = event.resultIndex; i < event.results.length; ++i) {
274
+ if (event.results[i].isFinal) {
275
+ coderFinal_transcript += event.results[i][0].transcript;
276
+ }
277
+ }
278
+ coderFinal_transcript = coderFinal_transcript.trim();
279
+ logMessage(`Recognized Speech: ${coderFinal_transcript}`, "user");
280
+ };
281
+ }
282
+
283
+ document.getElementById("coder-start_button").addEventListener("click", function(event) {
284
+ coderStartButton(event);
285
+ });
286
+
287
+ // Initialize Model Selection
288
+ coderAvailableModels.forEach((modelId) => {
289
+ const option = document.createElement("option");
290
+ option.value = modelId;
291
+ option.textContent = modelId;
292
+ document.getElementById("coder-model-selection").appendChild(option);
293
+ });
294
+ document.getElementById("coder-model-selection").value = coderSelectedModel;
295
+
296
+ // **Enable the Download Model button after models are loaded**
297
+ document.getElementById("coder-download").disabled = false;
298
+
299
+ document.getElementById("coder-download").addEventListener("click", function () {
300
+ coderInitializeWebLLMEngine().then(() => {
301
+ document.getElementById("coder-start_button").disabled = false;
302
+ // Enable speech controls after model initialization
303
+ document.getElementById("coder-speech-rate").disabled = false;
304
+ document.getElementById("coder-speech-pitch").disabled = false;
305
+ logMessage("Model download initiated.", "system");
306
+ });
307
+ });
308
+
309
+ document.getElementById("coder-clear-logs").addEventListener("click", function () {
310
+ document.getElementById("coder-logs").innerHTML = '';
311
+ logMessage("Logs cleared.", "system");
312
+ });
313
+
314
+ // ===== TTS Integration =====
315
+
316
+ // Initialize Speech Synthesis
317
+ let coderSpeech = new SpeechSynthesisUtterance();
318
+ coderSpeech.lang = "en";
319
+
320
+ let coderVoices = [];
321
+
322
+ // Use addEventListener instead of directly assigning to onvoiceschanged
323
+ window.speechSynthesis.addEventListener("voiceschanged", () => {
324
+ coderVoices = window.speechSynthesis.getVoices();
325
+ coderPopulateVoices();
326
+ });
327
+
328
+ function coderPopulateVoices() {
329
+ const voiceSelect = document.getElementById("coder-tools");
330
+ voiceSelect.innerHTML = ''; // Clear existing options
331
+ coderVoices.forEach((voice, i) => {
332
+ const option = new Option(voice.name, i);
333
+ voiceSelect.appendChild(option);
334
+ });
335
+ if (coderVoices.length > 0) {
336
+ const savedVoice = localStorage.getItem("coderSelectedVoice");
337
+ if (savedVoice !== null && coderVoices[savedVoice]) {
338
+ coderSpeech.voice = coderVoices[savedVoice];
339
+ voiceSelect.value = savedVoice;
340
+ } else {
341
+ coderSpeech.voice = coderVoices[0];
342
+ }
343
+ }
344
+ }
345
+
346
+ // Voice Selection Event Listener
347
+ document.getElementById("coder-tools").addEventListener("change", () => {
348
+ const selectedVoiceIndex = document.getElementById("coder-tools").value;
349
+ coderSpeech.voice = coderVoices[selectedVoiceIndex];
350
+ // Save to localStorage
351
+ localStorage.setItem("coderSelectedVoice", selectedVoiceIndex);
352
+ logMessage(`Voice changed to: ${coderVoices[selectedVoiceIndex].name}`, "system");
353
+ });
354
+
355
+ // Function to Speak Text with Voice Selection and Handling Large Texts
356
+ function coderSpeak(text) {
357
+ if (!window.speechSynthesis) {
358
+ console.warn("Speech Synthesis not supported in this browser for Coder section.");
359
+ logMessage("Speech Synthesis not supported in this browser.", "error");
360
+ return;
361
+ }
362
+
363
+ // Show spinner and enable Stop button
364
+ document.getElementById("coder-loading-spinner").classList.remove("hidden");
365
+ document.getElementById("coder-stop_button").disabled = false;
366
+ logMessage("TTS started.", "system");
367
+
368
+ // Retrieve the currently selected voice
369
+ const selectedVoice = coderSpeech.voice;
370
+
371
+ // Split the text into sentences to manage large texts
372
+ const sentences = text.match(/[^\.!\?]+[\.!\?]+/g) || [text];
373
+
374
+ let utterancesCount = sentences.length;
375
+
376
+ sentences.forEach(sentence => {
377
+ const utterance = new SpeechSynthesisUtterance(sentence.trim());
378
+
379
+ // Assign the selected voice to the utterance
380
+ if (selectedVoice) {
381
+ utterance.voice = selectedVoice;
382
+ }
383
+
384
+ // Assign rate and pitch from sliders
385
+ const rate = parseFloat(document.getElementById("coder-speech-rate").value);
386
+ const pitch = parseFloat(document.getElementById("coder-speech-pitch").value);
387
+ utterance.rate = rate; // Adjust the speaking rate (0.1 to 10)
388
+ utterance.pitch = pitch; // Adjust the pitch (0 to 2)
389
+
390
+ // Add event listeners for debugging or additional functionality
391
+ utterance.onstart = () => {
392
+ console.log("Speech started:", sentence);
393
+ logMessage(`TTS started: ${sentence.trim()}`, "system");
394
+ };
395
+
396
+ utterance.onend = () => {
397
+ console.log("Speech ended:", sentence);
398
+ logMessage(`TTS ended: ${sentence.trim()}`, "system");
399
+ utterancesCount--;
400
+ if (utterancesCount === 0) {
401
+ // Hide spinner and disable Stop button when all utterances have been spoken
402
+ document.getElementById("coder-loading-spinner").classList.add("hidden");
403
+ document.getElementById("coder-stop_button").disabled = true;
404
+ logMessage("All TTS messages have been spoken.", "system");
405
+ }
406
+ };
407
+
408
+ utterance.onerror = (e) => {
409
+ console.error("Speech Synthesis Error:", e);
410
+ alert("An error occurred during speech synthesis. Please try again.");
411
+ logMessage("Speech synthesis encountered an error.", "error");
412
+ utterancesCount = 0;
413
+ document.getElementById("coder-loading-spinner").classList.add("hidden");
414
+ document.getElementById("coder-stop_button").disabled = true;
415
+ };
416
+
417
+ window.speechSynthesis.speak(utterance);
418
+ });
419
+ }
420
+
421
+ // ===== New: Stop Speech Functionality =====
422
+
423
+ /**
424
+ * Stops any ongoing speech synthesis.
425
+ */
426
+ function coderStopSpeech() {
427
+ if (window.speechSynthesis.speaking) {
428
+ window.speechSynthesis.cancel();
429
+ document.getElementById("coder-loading-spinner").classList.add("hidden");
430
+ document.getElementById("coder-stop_button").disabled = true;
431
+ logMessage("Speech synthesis stopped by user.", "system");
432
+ }
433
+ }
434
+
435
+ // Event Listener for Stop Button
436
+ document.getElementById("coder-stop_button").addEventListener("click", function () {
437
+ coderStopSpeech();
438
+ });
439
+
440
+ // ===== New: Text Input Handling =====
441
+
442
+ // Function to Handle Text Submission
443
+ function coderHandleTextSubmit() {
444
+ const textInput = document.getElementById("coder-text-input");
445
+ const input = textInput.value.trim();
446
+ if (input.length === 0) {
447
+ return;
448
+ }
449
+ textInput.value = ''; // Clear the input field
450
+ const message = {
451
+ content: input,
452
+ role: "user" // Ensure this is correctly set
453
+ };
454
+ console.log(`Coder Text input received: ${input}`); // Debugging
455
+ logMessage(`User: ${input}`, "user");
456
+
457
+ coderLastInputWasVoice = false; // Set flag as text input
458
+ document.getElementById("coder-submit-button").disabled = true; // Disable to prevent multiple submissions
459
+
460
+ coderMessages.push(message);
461
+ coderAppendMessage(message);
462
+
463
+ // Append "typing..." placeholder
464
+ const aiPlaceholder = {
465
+ content: "typing...",
466
+ role: "assistant"
467
+ };
468
+ coderAppendMessage(aiPlaceholder);
469
+ logMessage("CoderBot is typing...", "system");
470
+
471
+ const onFinishGenerating = (finalMessage) => {
472
+ console.log(`Coder Finishing generation with message: ${finalMessage}`); // Debugging
473
+ // Remove the "typing..." placeholder
474
+ const coderChatBox = document.getElementById("coder-chat-box");
475
+ const lastMessageContainer = coderChatBox.lastElementChild;
476
+ if (lastMessageContainer && lastMessageContainer.querySelector(".message").textContent === "typing...") {
477
+ coderChatBox.removeChild(lastMessageContainer);
478
+ }
479
+
480
+ // Append the final message
481
+ const aiMessage = {
482
+ content: finalMessage,
483
+ role: "assistant"
484
+ };
485
+ coderAppendMessage(aiMessage);
486
+ logMessage(`CoderBot: ${finalMessage}`, "assistant");
487
+
488
+ // Trigger TTS for assistant messages if required
489
+ if (coderLastInputWasVoice) {
490
+ coderSpeak(finalMessage);
491
+ }
492
+
493
+ document.getElementById("coder-submit-button").disabled = false; // Re-enable submit button after processing
494
+ coderEngine.runtimeStatsText().then((statsText) => {
495
+ document.getElementById("coder-chat-stats").classList.remove("hidden");
496
+ document.getElementById("coder-chat-stats").textContent = statsText;
497
+ logMessage(`Runtime Stats: ${statsText}`, "system");
498
+ });
499
+ };
500
+
501
+ coderStreamingGenerating(
502
+ coderMessages,
503
+ coderUpdateLastMessage,
504
+ onFinishGenerating,
505
+ (err) => {
506
+ console.error(err);
507
+ alert("An error occurred while generating the response. Please try again.");
508
+ logMessage("Error during response generation.", "error");
509
+ document.getElementById("coder-submit-button").disabled = false;
510
+ }
511
+ );
512
+ }
513
+
514
+ // Event Listener for Submit Button
515
+ document.getElementById("coder-submit-button").addEventListener("click", function () {
516
+ coderHandleTextSubmit();
517
+ });
518
+
519
+ // Event Listener for Enter Key in Text Input
520
+ document.getElementById("coder-text-input").addEventListener("keypress", function (e) {
521
+ if (e.key === 'Enter') {
522
+ coderHandleTextSubmit();
523
+ }
524
+ });
525
+
526
+ // ===== Persisting User Preferences =====
527
+
528
+ // Load Preferences on Initialization
529
+ window.addEventListener("load", () => {
530
+ const savedVoice = localStorage.getItem("coderSelectedVoice");
531
+ if (savedVoice !== null && coderVoices[savedVoice]) {
532
+ document.getElementById("coder-tools").value = savedVoice;
533
+ coderSpeech.voice = coderVoices[savedVoice];
534
+ logMessage(`Loaded saved voice: ${coderVoices[savedVoice].name}`, "system");
535
+ }
536
+
537
+ const savedRate = localStorage.getItem("coderSpeechRate");
538
+ if (savedRate !== null) {
539
+ document.getElementById("coder-speech-rate").value = savedRate;
540
+ coderSpeech.rate = parseFloat(savedRate);
541
+ logMessage(`Loaded saved speech rate: ${savedRate}`, "system");
542
+ }
543
+
544
+ const savedPitch = localStorage.getItem("coderSpeechPitch");
545
+ if (savedPitch !== null) {
546
+ document.getElementById("coder-speech-pitch").value = savedPitch;
547
+ coderSpeech.pitch = parseFloat(savedPitch);
548
+ logMessage(`Loaded saved speech pitch: ${savedPitch}`, "system");
549
+ }
550
+ });
551
+
552
+ // Save Speech Rate
553
+ document.getElementById("coder-speech-rate").addEventListener("input", (e) => {
554
+ const rate = e.target.value;
555
+ coderSpeech.rate = parseFloat(rate);
556
+ localStorage.setItem("coderSpeechRate", rate);
557
+ logMessage(`Speech rate changed to: ${rate}`, "system");
558
+ });
559
+
560
+ // Save Speech Pitch
561
+ document.getElementById("coder-speech-pitch").addEventListener("input", (e) => {
562
+ const pitch = e.target.value;
563
+ coderSpeech.pitch = parseFloat(pitch);
564
+ localStorage.setItem("coderSpeechPitch", pitch);
565
+ logMessage(`Speech pitch changed to: ${pitch}`, "system");
566
+ });
567
+
568
+ // ===== Logging Function =====
569
+
570
+ /**
571
+ * Logs messages to the #coder-logs container.
572
+ * @param {string} message - The message to log.
573
+ * @param {string} type - The type of message: 'user', 'assistant', 'system', 'error'.
574
+ */
575
+ function logMessage(message, type) {
576
+ const coderLogs = document.getElementById("coder-logs");
577
+ const logEntry = document.createElement("div");
578
+ logEntry.classList.add("log-entry");
579
+ logEntry.textContent = `[${type.toUpperCase()}] ${message}`;
580
+
581
+ // Style log entries based on type
582
+ switch(type) {
583
+ case 'user':
584
+ logEntry.style.color = "#00796B";
585
+ break;
586
+ case 'assistant':
587
+ logEntry.style.color = "#004D40";
588
+ break;
589
+ case 'system':
590
+ logEntry.style.color = "#555555";
591
+ break;
592
+ case 'error':
593
+ logEntry.style.color = "#E53935";
594
+ break;
595
+ default:
596
+ logEntry.style.color = "#000000";
597
+ }
598
+
599
+ coderLogs.appendChild(logEntry);
600
+ coderLogs.scrollTop = coderLogs.scrollHeight;
601
+ }
602
+
603
+ // ===== TTS Integration Continued =====
604
+
605
+ // Optional: Global Listener to Detect When All Speech Has Finished
606
+ window.speechSynthesis.addEventListener('end', () => {
607
+ console.log("All coder speech has been spoken.");
608
+ logMessage("All TTS messages have been spoken.", "system");
609
+ // Ensure Stop button is disabled after speech ends
610
+ document.getElementById("coder-stop_button").disabled = true;
611
+ });
612
+ });
digital.human.audio.css ADDED
@@ -0,0 +1,422 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* digital.human.audio.css */
2
+
3
+ /* Reset and Base Styles */
4
+ #voice * {
5
+ box-sizing: border-box;
6
+ font-family: Arial, sans-serif;
7
+ }
8
+
9
+ /* Card Styling */
10
+ .voice-card {
11
+ display: flex;
12
+ gap: 20px;
13
+ background: linear-gradient(135deg, #ffffff, #f1f8e9);
14
+ border-radius: 16px;
15
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05);
16
+ padding: 25px;
17
+ margin: 25px auto;
18
+
19
+ transition: transform 0.3s, box-shadow 0.3s, background 0.3s;
20
+ }
21
+
22
+ .voice-card:hover {
23
+ transform: translateY(-5px);
24
+ box-shadow: 0 6px 25px rgba(0, 0, 0, 0.1);
25
+ background: linear-gradient(135deg, #e0f7fa, #ffffff);
26
+ }
27
+
28
+ /* Flex Layout for Voice Options and Chat Window */
29
+ .voice-options {
30
+ flex: 1;
31
+ display: flex;
32
+ flex-direction: column;
33
+ gap: 20px;
34
+ }
35
+
36
+ /* Ensure Voice Options takes up available vertical space */
37
+ .voice-options {
38
+ height: 100%;
39
+ }
40
+
41
+ /* Chat Window Styling */
42
+ .chat-window {
43
+ flex: 2;
44
+ display: flex;
45
+ flex-direction: column;
46
+ gap: 20px;
47
+ }
48
+
49
+ /* Option Sections Styling */
50
+ .option-section {
51
+ background-color: #ffffff;
52
+ padding: 15px;
53
+ border-radius: 12px;
54
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
55
+ transition: background 0.3s, box-shadow 0.3s;
56
+ }
57
+
58
+ .option-section:hover {
59
+ background-color: #f9f9f9;
60
+ }
61
+
62
+ /* Button Styling (Reused from Original) */
63
+ #voice button {
64
+ background: linear-gradient(135deg, #00796B, #004D40);
65
+ border: none;
66
+ color: white;
67
+ padding: 12px 20px;
68
+ font-size: 1em;
69
+ border-radius: 8px;
70
+ cursor: pointer;
71
+ transition: background 0.3s, transform 0.2s, box-shadow 0.3s;
72
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
73
+ }
74
+
75
+ #voice button:hover:not(:disabled) {
76
+ background: linear-gradient(135deg, #005D56, #00332E);
77
+ transform: translateY(-2px);
78
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
79
+ }
80
+
81
+ #voice button:disabled {
82
+ background: linear-gradient(135deg, #A5D6A7, #81C784);
83
+ cursor: not-allowed;
84
+ box-shadow: none;
85
+ }
86
+
87
+ /* Select Dropdown Styling (Reused from Original) */
88
+ #voice select {
89
+ width: 100%;
90
+ padding: 10px 15px;
91
+ border: 1px solid #cccccc;
92
+ border-radius: 8px;
93
+ font-size: 1em;
94
+ background-color: #ffffff;
95
+ transition: border-color 0.3s, box-shadow 0.3s;
96
+ margin-bottom: 15px;
97
+ }
98
+
99
+ #voice select:focus {
100
+ border-color: #00796B;
101
+ box-shadow: 0 0 5px rgba(0, 121, 107, 0.5);
102
+ outline: none;
103
+ }
104
+
105
+ /* Speech Controls Styling */
106
+ #voice-speech-controls label {
107
+ display: block;
108
+ margin-bottom: 5px;
109
+ color: #555555;
110
+ font-weight: 500;
111
+ }
112
+
113
+ #voice-speech-controls input[type="range"] {
114
+ width: 100%;
115
+ margin-bottom: 15px;
116
+ }
117
+
118
+ /* Logs Container Styling */
119
+ #voice-logs-container {
120
+ display: flex;
121
+ flex-direction: column;
122
+ gap: 10px;
123
+ max-height: 200px; /* Set a maximum height for the logs container */
124
+ overflow: hidden; /* Hide any overflow to prevent the container from expanding */
125
+ }
126
+
127
+ #voice-logs {
128
+ flex: 1;
129
+ max-height: 200px; /* Adjust as needed */
130
+ border: 1px solid #e0e0e0;
131
+ border-radius: 12px;
132
+ overflow-y: auto; /* Enable vertical scrolling */
133
+ padding: 10px;
134
+ background-color: #f9f9f9;
135
+ font-size: 0.85em;
136
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.05);
137
+ }
138
+
139
+ #voice-clear-logs {
140
+ align-self: flex-end;
141
+ background: linear-gradient(135deg, #FF5252, #E53935);
142
+ border: none;
143
+ color: white;
144
+ padding: 8px 16px;
145
+ font-size: 0.9em;
146
+ cursor: pointer;
147
+ border-radius: 8px;
148
+ transition: background 0.3s, transform 0.2s, box-shadow 0.3s;
149
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
150
+ }
151
+
152
+ #voice-clear-logs:hover {
153
+ background: linear-gradient(135deg, #E53935, #D32F2F);
154
+ transform: scale(1.02);
155
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
156
+ }
157
+
158
+ /* Chat Box Styling */
159
+ #voice-chat-box {
160
+ height: 500px;
161
+ border: 1px solid #e0e0e0;
162
+ border-radius: 12px;
163
+ overflow-y: auto;
164
+ padding: 20px;
165
+ background-color: #ffffff;
166
+ position: relative;
167
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.05);
168
+ margin-bottom: 20px;
169
+ }
170
+
171
+ /* Message Styling */
172
+ #voice .message-container {
173
+ margin-bottom: 20px;
174
+ display: flex;
175
+ }
176
+
177
+ #voice .message {
178
+ padding: 12px 18px;
179
+ border-radius: 20px;
180
+ max-width: 75%;
181
+ position: relative;
182
+ word-wrap: break-word;
183
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
184
+ font-size: 0.95em;
185
+ line-height: 1.4;
186
+ background-color: #f1f8e9;
187
+ transition: background-color 0.3s, box-shadow 0.3s;
188
+ }
189
+
190
+ #voice .user .message {
191
+ background: #DCF8C6;
192
+ border-bottom-right-radius: 0;
193
+ }
194
+
195
+ #voice .assistant .message {
196
+ background: #e3f2fd;
197
+ border-bottom-left-radius: 0;
198
+ }
199
+
200
+ /* Chat Input Container Styling */
201
+ #voice-text-input-container {
202
+ display: flex;
203
+ gap: 10px;
204
+ }
205
+
206
+ #voice-text-input {
207
+ flex: 1;
208
+ padding: 10px 15px;
209
+ border: 1px solid #cccccc;
210
+ border-radius: 8px;
211
+ font-size: 1em;
212
+ background-color: #ffffff;
213
+ transition: border-color 0.3s, box-shadow 0.3s;
214
+ }
215
+
216
+ #voice-text-input:focus {
217
+ border-color: #00796B;
218
+ box-shadow: 0 0 5px rgba(0, 121, 107, 0.5);
219
+ outline: none;
220
+ }
221
+
222
+ #voice-submit-button {
223
+ background: linear-gradient(135deg, #00796B, #004D40);
224
+ border: none;
225
+ color: white;
226
+ padding: 10px 20px;
227
+ font-size: 1em;
228
+ border-radius: 8px;
229
+ cursor: pointer;
230
+ transition: background 0.3s, transform 0.2s, box-shadow 0.3s;
231
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
232
+ }
233
+
234
+ #voice-submit-button:hover:not(:disabled) {
235
+ background: linear-gradient(135deg, #005D56, #00332E);
236
+ transform: translateY(-2px);
237
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
238
+ }
239
+
240
+ #voice-submit-button:disabled {
241
+ background: linear-gradient(135deg, #A5D6A7, #81C784);
242
+ cursor: not-allowed;
243
+ box-shadow: none;
244
+ }
245
+
246
+ /* Microphone and Stop Buttons Styling */
247
+ #voice-mic-container {
248
+ display: flex;
249
+ justify-content: center;
250
+ gap: 15px;
251
+ margin-top: 20px;
252
+ }
253
+
254
+ #voice-start_button,
255
+ #voice-stop_button {
256
+ background: linear-gradient(135deg, #00796B, #004D40);
257
+ border: none;
258
+ color: white;
259
+ padding: 15px;
260
+ border-radius: 50%;
261
+ cursor: pointer;
262
+ transition: background 0.3s, transform 0.2s, box-shadow 0.3s;
263
+ width: 60px;
264
+ height: 60px;
265
+ display: flex;
266
+ justify-content: center;
267
+ align-items: center;
268
+ position: relative;
269
+ overflow: hidden;
270
+ box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
271
+ }
272
+
273
+ #voice-stop_button {
274
+ background: linear-gradient(135deg, #E53935, #D32F2F);
275
+ }
276
+
277
+ #voice-start_button::before,
278
+ #voice-stop_button::before {
279
+ content: '';
280
+ position: absolute;
281
+ width: 200%;
282
+ height: 200%;
283
+ background: rgba(255, 255, 255, 0.3);
284
+ top: 50%;
285
+ left: 50%;
286
+ transform: translate(-50%, -50%) scale(0);
287
+ border-radius: 50%;
288
+ transition: transform 0.5s ease-out;
289
+ }
290
+
291
+ #voice-start_button:active::before,
292
+ #voice-stop_button:active::before {
293
+ transform: translate(-50%, -50%) scale(1);
294
+ }
295
+
296
+ #voice-start_button:disabled,
297
+ #voice-stop_button:disabled {
298
+ background: linear-gradient(135deg, #A5D6A7, #81C784);
299
+ cursor: not-allowed;
300
+ box-shadow: none;
301
+ }
302
+
303
+ #voice-start_button:hover:not(:disabled),
304
+ #voice-stop_button:hover:not(:disabled) {
305
+ transform: scale(1.05);
306
+ box-shadow: 0 6px 15px rgba(0, 0, 0, 0.15);
307
+ }
308
+
309
+ #voice-stop_button:hover:not(:disabled) {
310
+ background: linear-gradient(135deg, #D32F2F, #C62828);
311
+ }
312
+
313
+ /* Icon Styling */
314
+ #voice .mic-icon,
315
+ #voice .stop-icon {
316
+ width: 30px;
317
+ height: 30px;
318
+ transition: transform 0.3s;
319
+ fill: #ffffff;
320
+ }
321
+
322
+ /* Animation for Mic Icon */
323
+ #voice .mic-animate {
324
+ animation: pulse 2s infinite;
325
+ }
326
+
327
+ @keyframes pulse {
328
+ 0% {
329
+ transform: scale(1);
330
+ }
331
+ 50% {
332
+ transform: scale(1.2);
333
+ }
334
+ 100% {
335
+ transform: scale(1);
336
+ }
337
+ }
338
+
339
+ /* Chat Stats */
340
+ #voice-chat-stats {
341
+ font-size: 0.85em;
342
+ color: #555555;
343
+ text-align: center;
344
+ margin-top: 10px;
345
+ }
346
+
347
+ /* Hidden Class */
348
+ #voice .hidden {
349
+ display: none;
350
+ }
351
+
352
+ /* Scrollbar Styling */
353
+ #voice-chat-box::-webkit-scrollbar,
354
+ #voice-logs::-webkit-scrollbar {
355
+ width: 8px;
356
+ }
357
+
358
+ #voice-chat-box::-webkit-scrollbar-track,
359
+ #voice-logs::-webkit-scrollbar-track {
360
+ background: #f1f1f1;
361
+ border-radius: 4px;
362
+ }
363
+
364
+ #voice-chat-box::-webkit-scrollbar-thumb,
365
+ #voice-logs::-webkit-scrollbar-thumb {
366
+ background: #cccccc;
367
+ border-radius: 4px;
368
+ }
369
+
370
+ #voice-chat-box::-webkit-scrollbar-thumb:hover,
371
+ #voice-logs::-webkit-scrollbar-thumb:hover {
372
+ background: #a8a8a8;
373
+ }
374
+
375
+ /* Initialize Button Styling (Reused from Original) */
376
+ #voice-download {
377
+ flex: 0 0 auto;
378
+ background: linear-gradient(135deg, #00796B, #004D40);
379
+ border: none;
380
+ color: white;
381
+ padding: 10px 20px;
382
+ font-size: 1em;
383
+ border-radius: 8px;
384
+ cursor: pointer;
385
+ transition: background 0.3s, transform 0.2s, box-shadow 0.3s;
386
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
387
+ }
388
+
389
+ #voice-download:hover:not(:disabled) {
390
+ background: linear-gradient(135deg, #005D56, #00332E);
391
+ transform: translateY(-2px);
392
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
393
+ }
394
+
395
+ #voice-download:disabled {
396
+ background: linear-gradient(135deg, #A5D6A7, #81C784);
397
+ cursor: not-allowed;
398
+ box-shadow: none;
399
+ }
400
+
401
+ /* Configuration Info Styling */
402
+ #voice-configuration {
403
+ margin-top: 15px;
404
+ font-size: 0.9em;
405
+ color: #555555;
406
+ }
407
+
408
+ /* Responsive Grid Adjustments */
409
+ @media (max-width: 800px) {
410
+ .voice-card {
411
+ flex-direction: column;
412
+ }
413
+
414
+ /* Adjust Logs Container Height for Smaller Screens */
415
+ #voice-logs-container {
416
+ max-height: 200px;
417
+ }
418
+
419
+ #voice-logs {
420
+ max-height: 150px;
421
+ }
422
+ }
digital.human.audio.js ADDED
@@ -0,0 +1,612 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // digital.human.audio.js
2
+ import * as webllm from "https://esm.run/@mlc-ai/web-llm";
3
+
4
+ // Ensure the script runs after the DOM is fully loaded
5
+ document.addEventListener("DOMContentLoaded", () => {
6
+ // Initialize the Digital Human Voice section
7
+ const voiceMessages = [
8
+ {
9
+ content: "You are Aged Guru, an intelligent assistant skilled in digital human voice interactions. Provide insightful and comprehensive answers using human-like voice responses.",
10
+ role: "system"
11
+ }
12
+ ];
13
+
14
+ const voiceAvailableModels = webllm.prebuiltAppConfig.model_list.map(
15
+ (m) => m.model_id
16
+ );
17
+ let voiceSelectedModel = "gemma-2-2b-it-q4f16_1-MLC-1k"; // Default model
18
+
19
+ function voiceUpdateEngineInitProgressCallback(report) {
20
+ console.log("Digital Human Voice Initialize", report.progress);
21
+ // Instead of updating a status span, log the progress
22
+ logMessage(`Model Initialization Progress: ${report.text}`, "system");
23
+ }
24
+
25
+ const voiceEngine = new webllm.MLCEngine();
26
+ voiceEngine.setInitProgressCallback(voiceUpdateEngineInitProgressCallback);
27
+
28
+ let voiceIsGenerating = false; // Flag to prevent multiple generations
29
+
30
+ async function voiceInitializeWebLLMEngine() {
31
+ logMessage("Model initialization started.", "system");
32
+ document.getElementById("voice-loading-spinner").classList.remove("hidden"); // Show spinner
33
+ voiceSelectedModel = document.getElementById("voice-model-selection").value;
34
+ const config = {
35
+ temperature: 0.7, // Adjusted for more precise answers
36
+ top_p: 0.9
37
+ };
38
+ try {
39
+ await voiceEngine.reload(voiceSelectedModel, config);
40
+ document.getElementById("voice-selected-model").textContent = voiceSelectedModel;
41
+ document.getElementById("voice-start_button").disabled = false;
42
+ document.getElementById("voice-text-input").disabled = false; // Enable text input after initialization
43
+ document.getElementById("voice-submit-button").disabled = false; // Enable submit button after initialization
44
+ document.getElementById("voice-speech-controls").disabled = false; // Enable speech controls after initialization
45
+ document.getElementById("voice-configuration").classList.remove("hidden");
46
+ logMessage("Model initialized successfully.", "system");
47
+ } catch (error) {
48
+ console.error("Error initializing the model:", error);
49
+ alert("Failed to initialize the model. Please try again.");
50
+ logMessage("Failed to initialize the model.", "error");
51
+ } finally {
52
+ document.getElementById("voice-loading-spinner").classList.add("hidden"); // Hide spinner
53
+ }
54
+ }
55
+
56
+ async function voiceStreamingGenerating(messages, onUpdate, onFinish, onError) {
57
+ if (voiceIsGenerating) {
58
+ console.warn("Voice Generation already in progress.");
59
+ return;
60
+ }
61
+ voiceIsGenerating = true;
62
+ try {
63
+ let curMessage = "";
64
+ const completion = await voiceEngine.chat.completions.create({
65
+ stream: true,
66
+ messages
67
+ });
68
+ for await (const chunk of completion) {
69
+ const curDelta = chunk.choices[0].delta.content;
70
+ if (curDelta) {
71
+ curMessage += curDelta;
72
+ }
73
+ onUpdate(curMessage);
74
+ }
75
+ const finalMessage = await voiceEngine.getMessage();
76
+ console.log(`Voice Generated final message: ${finalMessage}`); // Debugging
77
+ onFinish(finalMessage);
78
+ logMessage("Response generated successfully.", "system");
79
+ } catch (err) {
80
+ console.error(err);
81
+ onError(err);
82
+ logMessage("An error occurred during response generation.", "error");
83
+ } finally {
84
+ voiceIsGenerating = false;
85
+ }
86
+ }
87
+
88
+ // Flag to track the last input method
89
+ let voiceLastInputWasVoice = false;
90
+
91
+ function voiceAppendMessage(message) {
92
+ console.log(`Voice Appending message: ${message.content} (Role: ${message.role})`); // Debugging
93
+ const voiceChatBox = document.getElementById("voice-chat-box");
94
+
95
+ // Check if the assistant's message is already appended to avoid duplication
96
+ if (message.role === "assistant") {
97
+ const existingMessages = voiceChatBox.querySelectorAll(".message");
98
+ const lastMessage = existingMessages[existingMessages.length - 1];
99
+ if (lastMessage && lastMessage.textContent === message.content) {
100
+ console.warn("Duplicate assistant message detected in Voice section, skipping append.");
101
+
102
+ // Only trigger TTS for assistant messages if the last input was via voice
103
+ if (message.role === "assistant" && message.content !== "typing..." && voiceLastInputWasVoice) {
104
+ voiceSpeak(message.content);
105
+ }
106
+
107
+ return; // Exit to avoid appending the same message twice
108
+ }
109
+ }
110
+
111
+ const container = document.createElement("div");
112
+ container.classList.add("message-container");
113
+ const newMessage = document.createElement("div");
114
+ newMessage.classList.add("message");
115
+ newMessage.textContent = message.content;
116
+
117
+ if (message.role === "user") {
118
+ container.classList.add("user");
119
+ } else {
120
+ container.classList.add("assistant");
121
+ }
122
+
123
+ container.appendChild(newMessage);
124
+ voiceChatBox.appendChild(container);
125
+ voiceChatBox.scrollTop = voiceChatBox.scrollHeight;
126
+
127
+ // Only trigger TTS for assistant messages if the last input was via voice
128
+ if (message.role === "assistant" && message.content !== "typing..." && voiceLastInputWasVoice) {
129
+ voiceSpeak(message.content);
130
+ }
131
+ }
132
+
133
+ function voiceUpdateLastMessage(content) {
134
+ const messageDoms = document.getElementById("voice-chat-box").querySelectorAll(".message");
135
+ const lastMessageDom = messageDoms[messageDoms.length - 1];
136
+ lastMessageDom.textContent = content;
137
+ }
138
+
139
+ function voiceOnSpeechRecognized(transcript) {
140
+ const input = transcript.trim();
141
+ const message = {
142
+ content: input,
143
+ role: "user"
144
+ };
145
+ if (input.length === 0) {
146
+ return;
147
+ }
148
+ voiceLastInputWasVoice = true; // Set flag as voice input
149
+ console.log(`Voice input received: ${input}`); // Debugging
150
+ document.getElementById("voice-start_button").disabled = true;
151
+ document.getElementById("voice-submit-button").disabled = true; // Disable submit button during processing
152
+
153
+ voiceMessages.push(message);
154
+ voiceAppendMessage(message);
155
+ logMessage(`User (Voice): ${input}`, "user");
156
+
157
+ // Append "typing..." placeholder
158
+ const aiPlaceholder = {
159
+ content: "typing...",
160
+ role: "assistant"
161
+ };
162
+ voiceAppendMessage(aiPlaceholder);
163
+ logMessage("VoiceBot is typing...", "system");
164
+
165
+ const onFinishGenerating = (finalMessage) => {
166
+ console.log(`Voice Finishing generation with message: ${finalMessage}`); // Debugging
167
+ // Remove the "typing..." placeholder
168
+ const voiceChatBox = document.getElementById("voice-chat-box");
169
+ const lastMessageContainer = voiceChatBox.lastElementChild;
170
+ if (lastMessageContainer && lastMessageContainer.querySelector(".message").textContent === "typing...") {
171
+ voiceChatBox.removeChild(lastMessageContainer);
172
+ }
173
+
174
+ // Append the final message
175
+ const aiMessage = {
176
+ content: finalMessage,
177
+ role: "assistant"
178
+ };
179
+ voiceAppendMessage(aiMessage);
180
+ logMessage(`VoiceBot: ${finalMessage}`, "assistant");
181
+
182
+ document.getElementById("voice-start_button").disabled = false;
183
+ document.getElementById("voice-submit-button").disabled = false; // Re-enable submit button after processing
184
+ voiceEngine.runtimeStatsText().then((statsText) => {
185
+ document.getElementById("voice-chat-stats").classList.remove("hidden");
186
+ document.getElementById("voice-chat-stats").textContent = statsText;
187
+ logMessage(`Runtime Stats: ${statsText}`, "system");
188
+ });
189
+ };
190
+
191
+ voiceStreamingGenerating(
192
+ voiceMessages,
193
+ voiceUpdateLastMessage,
194
+ onFinishGenerating,
195
+ (err) => {
196
+ console.error(err);
197
+ alert("An error occurred while generating the response. Please try again.");
198
+ logMessage("Error during response generation.", "error");
199
+ document.getElementById("voice-start_button").disabled = false;
200
+ document.getElementById("voice-submit-button").disabled = false;
201
+ }
202
+ );
203
+ }
204
+
205
+ // Speech Recognition Code for Voice
206
+ let voiceRecognizing = false;
207
+ let voiceIgnore_onend;
208
+ let voiceFinal_transcript = '';
209
+ let voiceRecognition;
210
+
211
+ function voiceStartButton(event) {
212
+ if (voiceRecognizing) {
213
+ voiceRecognition.stop();
214
+ return;
215
+ }
216
+ voiceFinal_transcript = '';
217
+ voiceRecognition.lang = 'en-US';
218
+ voiceRecognition.start();
219
+ voiceIgnore_onend = false;
220
+ document.getElementById("voice-start_button").classList.add("mic-animate");
221
+ logMessage("Voice input started.", "system");
222
+ }
223
+
224
+ if (!('webkitSpeechRecognition' in window)) {
225
+ alert("Web Speech API is not supported by this browser.");
226
+ logMessage("Web Speech API is not supported by this browser.", "error");
227
+ } else {
228
+ voiceRecognition = new webkitSpeechRecognition();
229
+ voiceRecognition.continuous = false; // Non-continuous recognition
230
+ voiceRecognition.interimResults = false; // Get only final results
231
+
232
+ voiceRecognition.onstart = function() {
233
+ voiceRecognizing = true;
234
+ logMessage("Speech recognition started.", "system");
235
+ };
236
+
237
+ voiceRecognition.onerror = function(event) {
238
+ if (event.error == 'no-speech') {
239
+ document.getElementById("voice-start_button").classList.remove("mic-animate");
240
+ alert('No speech was detected in Voice section.');
241
+ logMessage("No speech detected.", "error");
242
+ voiceIgnore_onend = true;
243
+ }
244
+ if (event.error == 'audio-capture') {
245
+ document.getElementById("voice-start_button").classList.remove("mic-animate");
246
+ alert('No microphone was found in Voice section.');
247
+ logMessage("No microphone found.", "error");
248
+ voiceIgnore_onend = true;
249
+ }
250
+ if (event.error == 'not-allowed') {
251
+ alert('Permission to use microphone was denied in Voice section.');
252
+ logMessage("Microphone permission denied.", "error");
253
+ voiceIgnore_onend = true;
254
+ }
255
+ };
256
+
257
+ voiceRecognition.onend = function() {
258
+ voiceRecognizing = false;
259
+ document.getElementById("voice-start_button").classList.remove("mic-animate");
260
+ logMessage("Speech recognition ended.", "system");
261
+ if (voiceIgnore_onend) {
262
+ return;
263
+ }
264
+ if (!voiceFinal_transcript) {
265
+ logMessage("No transcript captured.", "error");
266
+ return;
267
+ }
268
+ // Process the final transcript
269
+ voiceOnSpeechRecognized(voiceFinal_transcript);
270
+ };
271
+
272
+ voiceRecognition.onresult = function(event) {
273
+ for (let i = event.resultIndex; i < event.results.length; ++i) {
274
+ if (event.results[i].isFinal) {
275
+ voiceFinal_transcript += event.results[i][0].transcript;
276
+ }
277
+ }
278
+ voiceFinal_transcript = voiceFinal_transcript.trim();
279
+ logMessage(`Recognized Speech: ${voiceFinal_transcript}`, "user");
280
+ };
281
+ }
282
+
283
+ document.getElementById("voice-start_button").addEventListener("click", function(event) {
284
+ voiceStartButton(event);
285
+ });
286
+
287
+ // Initialize Model Selection
288
+ voiceAvailableModels.forEach((modelId) => {
289
+ const option = document.createElement("option");
290
+ option.value = modelId;
291
+ option.textContent = modelId;
292
+ document.getElementById("voice-model-selection").appendChild(option);
293
+ });
294
+ document.getElementById("voice-model-selection").value = voiceSelectedModel;
295
+
296
+ // **Enable the Download Model button after models are loaded**
297
+ document.getElementById("voice-download").disabled = false;
298
+
299
+ document.getElementById("voice-download").addEventListener("click", function () {
300
+ voiceInitializeWebLLMEngine().then(() => {
301
+ document.getElementById("voice-start_button").disabled = false;
302
+ // Enable speech controls after model initialization
303
+ document.getElementById("voice-speech-rate").disabled = false;
304
+ document.getElementById("voice-speech-pitch").disabled = false;
305
+ logMessage("Model download initiated.", "system");
306
+ });
307
+ });
308
+
309
+ document.getElementById("voice-clear-logs").addEventListener("click", function () {
310
+ document.getElementById("voice-logs").innerHTML = '';
311
+ logMessage("Logs cleared.", "system");
312
+ });
313
+
314
+ // ===== TTS Integration =====
315
+
316
+ // Initialize Speech Synthesis
317
+ let voiceSpeech = new SpeechSynthesisUtterance();
318
+ voiceSpeech.lang = "en";
319
+
320
+ let voiceVoices = [];
321
+
322
+ // Use addEventListener instead of directly assigning to onvoiceschanged
323
+ window.speechSynthesis.addEventListener("voiceschanged", () => {
324
+ voiceVoices = window.speechSynthesis.getVoices();
325
+ voicePopulateVoices();
326
+ });
327
+
328
+ function voicePopulateVoices() {
329
+ const voiceSelect = document.getElementById("voice-tools");
330
+ voiceSelect.innerHTML = ''; // Clear existing options
331
+ voiceVoices.forEach((voice, i) => {
332
+ const option = new Option(voice.name, i);
333
+ voiceSelect.appendChild(option);
334
+ });
335
+ if (voiceVoices.length > 0) {
336
+ const savedVoice = localStorage.getItem("voiceSelectedVoice");
337
+ if (savedVoice !== null && voiceVoices[savedVoice]) {
338
+ voiceSpeech.voice = voiceVoices[savedVoice];
339
+ voiceSelect.value = savedVoice;
340
+ } else {
341
+ voiceSpeech.voice = voiceVoices[0];
342
+ }
343
+ }
344
+ }
345
+
346
+ // Voice Selection Event Listener
347
+ document.getElementById("voice-tools").addEventListener("change", () => {
348
+ const selectedVoiceIndex = document.getElementById("voice-tools").value;
349
+ voiceSpeech.voice = voiceVoices[selectedVoiceIndex];
350
+ // Save to localStorage
351
+ localStorage.setItem("voiceSelectedVoice", selectedVoiceIndex);
352
+ logMessage(`Voice changed to: ${voiceVoices[selectedVoiceIndex].name}`, "system");
353
+ });
354
+
355
+ // Function to Speak Text with Voice Selection and Handling Large Texts
356
+ function voiceSpeak(text) {
357
+ if (!window.speechSynthesis) {
358
+ console.warn("Speech Synthesis not supported in this browser for Voice section.");
359
+ logMessage("Speech Synthesis not supported in this browser.", "error");
360
+ return;
361
+ }
362
+
363
+ // Show spinner and enable Stop button
364
+ document.getElementById("voice-loading-spinner").classList.remove("hidden");
365
+ document.getElementById("voice-stop_button").disabled = false;
366
+ logMessage("TTS started.", "system");
367
+
368
+ // Retrieve the currently selected voice
369
+ const selectedVoice = voiceSpeech.voice;
370
+
371
+ // Split the text into sentences to manage large texts
372
+ const sentences = text.match(/[^\.!\?]+[\.!\?]+/g) || [text];
373
+
374
+ let utterancesCount = sentences.length;
375
+
376
+ sentences.forEach(sentence => {
377
+ const utterance = new SpeechSynthesisUtterance(sentence.trim());
378
+
379
+ // Assign the selected voice to the utterance
380
+ if (selectedVoice) {
381
+ utterance.voice = selectedVoice;
382
+ }
383
+
384
+ // Assign rate and pitch from sliders
385
+ const rate = parseFloat(document.getElementById("voice-speech-rate").value);
386
+ const pitch = parseFloat(document.getElementById("voice-speech-pitch").value);
387
+ utterance.rate = rate; // Adjust the speaking rate (0.1 to 10)
388
+ utterance.pitch = pitch; // Adjust the pitch (0 to 2)
389
+
390
+ // Add event listeners for debugging or additional functionality
391
+ utterance.onstart = () => {
392
+ console.log("Speech started:", sentence);
393
+ logMessage(`TTS started: ${sentence.trim()}`, "system");
394
+ };
395
+
396
+ utterance.onend = () => {
397
+ console.log("Speech ended:", sentence);
398
+ logMessage(`TTS ended: ${sentence.trim()}`, "system");
399
+ utterancesCount--;
400
+ if (utterancesCount === 0) {
401
+ // Hide spinner and disable Stop button when all utterances have been spoken
402
+ document.getElementById("voice-loading-spinner").classList.add("hidden");
403
+ document.getElementById("voice-stop_button").disabled = true;
404
+ logMessage("All TTS messages have been spoken.", "system");
405
+ }
406
+ };
407
+
408
+ utterance.onerror = (e) => {
409
+ console.error("Speech Synthesis Error:", e);
410
+ alert("An error occurred during speech synthesis. Please try again.");
411
+ logMessage("Speech synthesis encountered an error.", "error");
412
+ utterancesCount = 0;
413
+ document.getElementById("voice-loading-spinner").classList.add("hidden");
414
+ document.getElementById("voice-stop_button").disabled = true;
415
+ };
416
+
417
+ window.speechSynthesis.speak(utterance);
418
+ });
419
+ }
420
+
421
+ // ===== New: Stop Speech Functionality =====
422
+
423
+ /**
424
+ * Stops any ongoing speech synthesis.
425
+ */
426
+ function voiceStopSpeech() {
427
+ if (window.speechSynthesis.speaking) {
428
+ window.speechSynthesis.cancel();
429
+ document.getElementById("voice-loading-spinner").classList.add("hidden");
430
+ document.getElementById("voice-stop_button").disabled = true;
431
+ logMessage("Speech synthesis stopped by user.", "system");
432
+ }
433
+ }
434
+
435
+ // Event Listener for Stop Button
436
+ document.getElementById("voice-stop_button").addEventListener("click", function () {
437
+ voiceStopSpeech();
438
+ });
439
+
440
+ // ===== New: Text Input Handling =====
441
+
442
+ // Function to Handle Text Submission
443
+ function voiceHandleTextSubmit() {
444
+ const textInput = document.getElementById("voice-text-input");
445
+ const input = textInput.value.trim();
446
+ if (input.length === 0) {
447
+ return;
448
+ }
449
+ textInput.value = ''; // Clear the input field
450
+ const message = {
451
+ content: input,
452
+ role: "user" // Ensure this is correctly set
453
+ };
454
+ console.log(`Voice Text input received: ${input}`); // Debugging
455
+ logMessage(`User: ${input}`, "user");
456
+
457
+ voiceLastInputWasVoice = false; // Set flag as text input
458
+ document.getElementById("voice-submit-button").disabled = true; // Disable to prevent multiple submissions
459
+
460
+ voiceMessages.push(message);
461
+ voiceAppendMessage(message);
462
+
463
+ // Append "typing..." placeholder
464
+ const aiPlaceholder = {
465
+ content: "typing...",
466
+ role: "assistant"
467
+ };
468
+ voiceAppendMessage(aiPlaceholder);
469
+ logMessage("VoiceBot is typing...", "system");
470
+
471
+ const onFinishGenerating = (finalMessage) => {
472
+ console.log(`Voice Finishing generation with message: ${finalMessage}`); // Debugging
473
+ // Remove the "typing..." placeholder
474
+ const voiceChatBox = document.getElementById("voice-chat-box");
475
+ const lastMessageContainer = voiceChatBox.lastElementChild;
476
+ if (lastMessageContainer && lastMessageContainer.querySelector(".message").textContent === "typing...") {
477
+ voiceChatBox.removeChild(lastMessageContainer);
478
+ }
479
+
480
+ // Append the final message
481
+ const aiMessage = {
482
+ content: finalMessage,
483
+ role: "assistant"
484
+ };
485
+ voiceAppendMessage(aiMessage);
486
+ logMessage(`VoiceBot: ${finalMessage}`, "assistant");
487
+
488
+ // Trigger TTS for assistant messages if required
489
+ if (voiceLastInputWasVoice) {
490
+ voiceSpeak(finalMessage);
491
+ }
492
+
493
+ document.getElementById("voice-submit-button").disabled = false; // Re-enable submit button after processing
494
+ voiceEngine.runtimeStatsText().then((statsText) => {
495
+ document.getElementById("voice-chat-stats").classList.remove("hidden");
496
+ document.getElementById("voice-chat-stats").textContent = statsText;
497
+ logMessage(`Runtime Stats: ${statsText}`, "system");
498
+ });
499
+ };
500
+
501
+ voiceStreamingGenerating(
502
+ voiceMessages,
503
+ voiceUpdateLastMessage,
504
+ onFinishGenerating,
505
+ (err) => {
506
+ console.error(err);
507
+ alert("An error occurred while generating the response. Please try again.");
508
+ logMessage("Error during response generation.", "error");
509
+ document.getElementById("voice-submit-button").disabled = false;
510
+ }
511
+ );
512
+ }
513
+
514
+ // Event Listener for Submit Button
515
+ document.getElementById("voice-submit-button").addEventListener("click", function () {
516
+ voiceHandleTextSubmit();
517
+ });
518
+
519
+ // Event Listener for Enter Key in Text Input
520
+ document.getElementById("voice-text-input").addEventListener("keypress", function (e) {
521
+ if (e.key === 'Enter') {
522
+ voiceHandleTextSubmit();
523
+ }
524
+ });
525
+
526
+ // ===== Persisting User Preferences =====
527
+
528
+ // Load Preferences on Initialization
529
+ window.addEventListener("load", () => {
530
+ const savedVoice = localStorage.getItem("voiceSelectedVoice");
531
+ if (savedVoice !== null && voiceVoices[savedVoice]) {
532
+ document.getElementById("voice-tools").value = savedVoice;
533
+ voiceSpeech.voice = voiceVoices[savedVoice];
534
+ logMessage(`Loaded saved voice: ${voiceVoices[savedVoice].name}`, "system");
535
+ }
536
+
537
+ const savedRate = localStorage.getItem("voiceSpeechRate");
538
+ if (savedRate !== null) {
539
+ document.getElementById("voice-speech-rate").value = savedRate;
540
+ voiceSpeech.rate = parseFloat(savedRate);
541
+ logMessage(`Loaded saved speech rate: ${savedRate}`, "system");
542
+ }
543
+
544
+ const savedPitch = localStorage.getItem("voiceSpeechPitch");
545
+ if (savedPitch !== null) {
546
+ document.getElementById("voice-speech-pitch").value = savedPitch;
547
+ voiceSpeech.pitch = parseFloat(savedPitch);
548
+ logMessage(`Loaded saved speech pitch: ${savedPitch}`, "system");
549
+ }
550
+ });
551
+
552
+ // Save Speech Rate
553
+ document.getElementById("voice-speech-rate").addEventListener("input", (e) => {
554
+ const rate = e.target.value;
555
+ voiceSpeech.rate = parseFloat(rate);
556
+ localStorage.setItem("voiceSpeechRate", rate);
557
+ logMessage(`Speech rate changed to: ${rate}`, "system");
558
+ });
559
+
560
+ // Save Speech Pitch
561
+ document.getElementById("voice-speech-pitch").addEventListener("input", (e) => {
562
+ const pitch = e.target.value;
563
+ voiceSpeech.pitch = parseFloat(pitch);
564
+ localStorage.setItem("voiceSpeechPitch", pitch);
565
+ logMessage(`Speech pitch changed to: ${pitch}`, "system");
566
+ });
567
+
568
+ // ===== Logging Function =====
569
+
570
+ /**
571
+ * Logs messages to the #voice-logs container.
572
+ * @param {string} message - The message to log.
573
+ * @param {string} type - The type of message: 'user', 'assistant', 'system', 'error'.
574
+ */
575
+ function logMessage(message, type) {
576
+ const voiceLogs = document.getElementById("voice-logs");
577
+ const logEntry = document.createElement("div");
578
+ logEntry.classList.add("log-entry");
579
+ logEntry.textContent = `[${type.toUpperCase()}] ${message}`;
580
+
581
+ // Style log entries based on type
582
+ switch(type) {
583
+ case 'user':
584
+ logEntry.style.color = "#00796B";
585
+ break;
586
+ case 'assistant':
587
+ logEntry.style.color = "#004D40";
588
+ break;
589
+ case 'system':
590
+ logEntry.style.color = "#555555";
591
+ break;
592
+ case 'error':
593
+ logEntry.style.color = "#E53935";
594
+ break;
595
+ default:
596
+ logEntry.style.color = "#000000";
597
+ }
598
+
599
+ voiceLogs.appendChild(logEntry);
600
+ voiceLogs.scrollTop = voiceLogs.scrollHeight;
601
+ }
602
+
603
+ // ===== TTS Integration Continued =====
604
+
605
+ // Optional: Global Listener to Detect When All Speech Has Finished
606
+ window.speechSynthesis.addEventListener('end', () => {
607
+ console.log("All voice speech has been spoken.");
608
+ logMessage("All TTS messages have been spoken.", "system");
609
+ // Ensure Stop button is disabled after speech ends
610
+ document.getElementById("voice-stop_button").disabled = true;
611
+ });
612
+ });
digital.human.video.css ADDED
@@ -0,0 +1,422 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* digital.human.video.css */
2
+
3
+ /* Reset and Base Styles */
4
+ #video * {
5
+ box-sizing: border-box;
6
+ font-family: Arial, sans-serif;
7
+ }
8
+
9
+ /* Card Styling */
10
+ .video-card {
11
+ display: flex;
12
+ gap: 20px;
13
+ background: linear-gradient(135deg, #ffffff, #f1f8e9);
14
+ border-radius: 16px;
15
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05);
16
+ padding: 25px;
17
+ margin: 25px auto;
18
+
19
+ transition: transform 0.3s, box-shadow 0.3s, background 0.3s;
20
+ }
21
+
22
+ .video-card:hover {
23
+ transform: translateY(-5px);
24
+ box-shadow: 0 6px 25px rgba(0, 0, 0, 0.1);
25
+ background: linear-gradient(135deg, #e0f7fa, #ffffff);
26
+ }
27
+
28
+ /* Flex Layout for Video Options and Chat Window */
29
+ .video-options {
30
+ flex: 1;
31
+ display: flex;
32
+ flex-direction: column;
33
+ gap: 20px;
34
+ }
35
+
36
+ /* Ensure Video Options takes up available vertical space */
37
+ .video-options {
38
+ height: 100%;
39
+ }
40
+
41
+ /* Chat Window Styling */
42
+ .chat-window {
43
+ flex: 2;
44
+ display: flex;
45
+ flex-direction: column;
46
+ gap: 20px;
47
+ }
48
+
49
+ /* Option Sections Styling */
50
+ .option-section {
51
+ background-color: #ffffff;
52
+ padding: 15px;
53
+ border-radius: 12px;
54
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
55
+ transition: background 0.3s, box-shadow 0.3s;
56
+ }
57
+
58
+ .option-section:hover {
59
+ background-color: #f9f9f9;
60
+ }
61
+
62
+ /* Button Styling (Reused from Original) */
63
+ #video button {
64
+ background: linear-gradient(135deg, #00796B, #004D40);
65
+ border: none;
66
+ color: white;
67
+ padding: 12px 20px;
68
+ font-size: 1em;
69
+ border-radius: 8px;
70
+ cursor: pointer;
71
+ transition: background 0.3s, transform 0.2s, box-shadow 0.3s;
72
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
73
+ }
74
+
75
+ #video button:hover:not(:disabled) {
76
+ background: linear-gradient(135deg, #005D56, #00332E);
77
+ transform: translateY(-2px);
78
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
79
+ }
80
+
81
+ #video button:disabled {
82
+ background: linear-gradient(135deg, #A5D6A7, #81C784);
83
+ cursor: not-allowed;
84
+ box-shadow: none;
85
+ }
86
+
87
+ /* Select Dropdown Styling (Reused from Original) */
88
+ #video select {
89
+ width: 100%;
90
+ padding: 10px 15px;
91
+ border: 1px solid #cccccc;
92
+ border-radius: 8px;
93
+ font-size: 1em;
94
+ background-color: #ffffff;
95
+ transition: border-color 0.3s, box-shadow 0.3s;
96
+ margin-bottom: 15px;
97
+ }
98
+
99
+ #video select:focus {
100
+ border-color: #00796B;
101
+ box-shadow: 0 0 5px rgba(0, 121, 107, 0.5);
102
+ outline: none;
103
+ }
104
+
105
+ /* Speech Controls Styling */
106
+ #video-speech-controls label {
107
+ display: block;
108
+ margin-bottom: 5px;
109
+ color: #555555;
110
+ font-weight: 500;
111
+ }
112
+
113
+ #video-speech-controls input[type="range"] {
114
+ width: 100%;
115
+ margin-bottom: 15px;
116
+ }
117
+
118
+ /* Logs Container Styling */
119
+ #video-logs-container {
120
+ display: flex;
121
+ flex-direction: column;
122
+ gap: 10px;
123
+ max-height: 250px; /* Set a maximum height for the logs container */
124
+ overflow: hidden; /* Hide any overflow to prevent the container from expanding */
125
+ }
126
+
127
+ #video-logs {
128
+ flex: 1;
129
+ max-height: 200px; /* Adjust as needed */
130
+ border: 1px solid #e0e0e0;
131
+ border-radius: 12px;
132
+ overflow-y: auto; /* Enable vertical scrolling */
133
+ padding: 10px;
134
+ background-color: #f9f9f9;
135
+ font-size: 0.85em;
136
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.05);
137
+ }
138
+
139
+ #video-clear-logs {
140
+ align-self: flex-end;
141
+ background: linear-gradient(135deg, #FF5252, #E53935);
142
+ border: none;
143
+ color: white;
144
+ padding: 8px 16px;
145
+ font-size: 0.9em;
146
+ cursor: pointer;
147
+ border-radius: 8px;
148
+ transition: background 0.3s, transform 0.2s, box-shadow 0.3s;
149
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
150
+ }
151
+
152
+ #video-clear-logs:hover {
153
+ background: linear-gradient(135deg, #E53935, #D32F2F);
154
+ transform: scale(1.02);
155
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
156
+ }
157
+
158
+ /* Chat Box Styling */
159
+ #video-chat-box {
160
+ height: 500px;
161
+ border: 1px solid #e0e0e0;
162
+ border-radius: 12px;
163
+ overflow-y: auto;
164
+ padding: 20px;
165
+ background-color: #ffffff;
166
+ position: relative;
167
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.05);
168
+ margin-bottom: 20px;
169
+ }
170
+
171
+ /* Message Styling */
172
+ #video .message-container {
173
+ margin-bottom: 20px;
174
+ display: flex;
175
+ }
176
+
177
+ #video .message {
178
+ padding: 12px 18px;
179
+ border-radius: 20px;
180
+ max-width: 75%;
181
+ position: relative;
182
+ word-wrap: break-word;
183
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
184
+ font-size: 0.95em;
185
+ line-height: 1.4;
186
+ background-color: #f1f8e9;
187
+ transition: background-color 0.3s, box-shadow 0.3s;
188
+ }
189
+
190
+ #video .user .message {
191
+ background: #DCF8C6;
192
+ border-bottom-right-radius: 0;
193
+ }
194
+
195
+ #video .assistant .message {
196
+ background: #e3f2fd;
197
+ border-bottom-left-radius: 0;
198
+ }
199
+
200
+ /* Chat Input Container Styling */
201
+ #video-text-input-container {
202
+ display: flex;
203
+ gap: 10px;
204
+ }
205
+
206
+ #video-text-input {
207
+ flex: 1;
208
+ padding: 10px 15px;
209
+ border: 1px solid #cccccc;
210
+ border-radius: 8px;
211
+ font-size: 1em;
212
+ background-color: #ffffff;
213
+ transition: border-color 0.3s, box-shadow 0.3s;
214
+ }
215
+
216
+ #video-text-input:focus {
217
+ border-color: #00796B;
218
+ box-shadow: 0 0 5px rgba(0, 121, 107, 0.5);
219
+ outline: none;
220
+ }
221
+
222
+ #video-submit-button {
223
+ background: linear-gradient(135deg, #00796B, #004D40);
224
+ border: none;
225
+ color: white;
226
+ padding: 10px 20px;
227
+ font-size: 1em;
228
+ border-radius: 8px;
229
+ cursor: pointer;
230
+ transition: background 0.3s, transform 0.2s, box-shadow 0.3s;
231
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
232
+ }
233
+
234
+ #video-submit-button:hover:not(:disabled) {
235
+ background: linear-gradient(135deg, #005D56, #00332E);
236
+ transform: translateY(-2px);
237
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
238
+ }
239
+
240
+ #video-submit-button:disabled {
241
+ background: linear-gradient(135deg, #A5D6A7, #81C784);
242
+ cursor: not-allowed;
243
+ box-shadow: none;
244
+ }
245
+
246
+ /* Microphone and Stop Buttons Styling */
247
+ #video-mic-container {
248
+ display: flex;
249
+ justify-content: center;
250
+ gap: 15px;
251
+ margin-top: 20px;
252
+ }
253
+
254
+ #video-start_button,
255
+ #video-stop_button {
256
+ background: linear-gradient(135deg, #00796B, #004D40);
257
+ border: none;
258
+ color: white;
259
+ padding: 15px;
260
+ border-radius: 50%;
261
+ cursor: pointer;
262
+ transition: background 0.3s, transform 0.2s, box-shadow 0.3s;
263
+ width: 60px;
264
+ height: 60px;
265
+ display: flex;
266
+ justify-content: center;
267
+ align-items: center;
268
+ position: relative;
269
+ overflow: hidden;
270
+ box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
271
+ }
272
+
273
+ #video-stop_button {
274
+ background: linear-gradient(135deg, #E53935, #D32F2F);
275
+ }
276
+
277
+ #video-start_button::before,
278
+ #video-stop_button::before {
279
+ content: '';
280
+ position: absolute;
281
+ width: 200%;
282
+ height: 200%;
283
+ background: rgba(255, 255, 255, 0.3);
284
+ top: 50%;
285
+ left: 50%;
286
+ transform: translate(-50%, -50%) scale(0);
287
+ border-radius: 50%;
288
+ transition: transform 0.5s ease-out;
289
+ }
290
+
291
+ #video-start_button:active::before,
292
+ #video-stop_button:active::before {
293
+ transform: translate(-50%, -50%) scale(1);
294
+ }
295
+
296
+ #video-start_button:disabled,
297
+ #video-stop_button:disabled {
298
+ background: linear-gradient(135deg, #A5D6A7, #81C784);
299
+ cursor: not-allowed;
300
+ box-shadow: none;
301
+ }
302
+
303
+ #video-start_button:hover:not(:disabled),
304
+ #video-stop_button:hover:not(:disabled) {
305
+ transform: scale(1.05);
306
+ box-shadow: 0 6px 15px rgba(0, 0, 0, 0.15);
307
+ }
308
+
309
+ #video-stop_button:hover:not(:disabled) {
310
+ background: linear-gradient(135deg, #D32F2F, #C62828);
311
+ }
312
+
313
+ /* Icon Styling */
314
+ #video .mic-icon,
315
+ #video .stop-icon {
316
+ width: 30px;
317
+ height: 30px;
318
+ transition: transform 0.3s;
319
+ fill: #ffffff;
320
+ }
321
+
322
+ /* Animation for Mic Icon */
323
+ #video .mic-animate {
324
+ animation: pulse 2s infinite;
325
+ }
326
+
327
+ @keyframes pulse {
328
+ 0% {
329
+ transform: scale(1);
330
+ }
331
+ 50% {
332
+ transform: scale(1.2);
333
+ }
334
+ 100% {
335
+ transform: scale(1);
336
+ }
337
+ }
338
+
339
+ /* Chat Stats */
340
+ #video-chat-stats {
341
+ font-size: 0.85em;
342
+ color: #555555;
343
+ text-align: center;
344
+ margin-top: 10px;
345
+ }
346
+
347
+ /* Hidden Class */
348
+ #video .hidden {
349
+ display: none;
350
+ }
351
+
352
+ /* Scrollbar Styling */
353
+ #video-chat-box::-webkit-scrollbar,
354
+ #video-logs::-webkit-scrollbar {
355
+ width: 8px;
356
+ }
357
+
358
+ #video-chat-box::-webkit-scrollbar-track,
359
+ #video-logs::-webkit-scrollbar-track {
360
+ background: #f1f1f1;
361
+ border-radius: 4px;
362
+ }
363
+
364
+ #video-chat-box::-webkit-scrollbar-thumb,
365
+ #video-logs::-webkit-scrollbar-thumb {
366
+ background: #cccccc;
367
+ border-radius: 4px;
368
+ }
369
+
370
+ #video-chat-box::-webkit-scrollbar-thumb:hover,
371
+ #video-logs::-webkit-scrollbar-thumb:hover {
372
+ background: #a8a8a8;
373
+ }
374
+
375
+ /* Initialize Button Styling (Reused from Original) */
376
+ #video-download {
377
+ flex: 0 0 auto;
378
+ background: linear-gradient(135deg, #00796B, #004D40);
379
+ border: none;
380
+ color: white;
381
+ padding: 10px 20px;
382
+ font-size: 1em;
383
+ border-radius: 8px;
384
+ cursor: pointer;
385
+ transition: background 0.3s, transform 0.2s, box-shadow 0.3s;
386
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
387
+ }
388
+
389
+ #video-download:hover:not(:disabled) {
390
+ background: linear-gradient(135deg, #005D56, #00332E);
391
+ transform: translateY(-2px);
392
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
393
+ }
394
+
395
+ #video-download:disabled {
396
+ background: linear-gradient(135deg, #A5D6A7, #81C784);
397
+ cursor: not-allowed;
398
+ box-shadow: none;
399
+ }
400
+
401
+ /* Configuration Info Styling */
402
+ #video-configuration {
403
+ margin-top: 15px;
404
+ font-size: 0.9em;
405
+ color: #555555;
406
+ }
407
+
408
+ /* Responsive Grid Adjustments */
409
+ @media (max-width: 800px) {
410
+ .video-card {
411
+ flex-direction: column;
412
+ }
413
+
414
+ /* Adjust Logs Container Height for Smaller Screens */
415
+ #video-logs-container {
416
+ max-height: 200px;
417
+ }
418
+
419
+ #video-logs {
420
+ max-height: 150px;
421
+ }
422
+ }
digital.human.video.js ADDED
@@ -0,0 +1,612 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // digital.human.video.js
2
+ import * as webllm from "https://esm.run/@mlc-ai/web-llm";
3
+
4
+ // Ensure the script runs after the DOM is fully loaded
5
+ document.addEventListener("DOMContentLoaded", () => {
6
+ // Initialize the Digital Human Video Assistant section
7
+ const videoMessages = [
8
+ {
9
+ content: "You are Aged Guru, an intelligent assistant skilled in video analysis and related interdisciplinary studies. Provide insightful and comprehensive answers to complex video-related questions.",
10
+ role: "system"
11
+ }
12
+ ];
13
+
14
+ const videoAvailableModels = webllm.prebuiltAppConfig.model_list.map(
15
+ (m) => m.model_id
16
+ );
17
+ let videoSelectedModel = "Llama-3.2-3B-Instruct-q4f16_1-MLC"; // Default model
18
+
19
+ function videoUpdateEngineInitProgressCallback(report) {
20
+ console.log("Digital Human Video Initialize", report.progress);
21
+ // Instead of updating a status span, log the progress
22
+ logMessage(`Model Initialization Progress: ${report.text}`, "system");
23
+ }
24
+
25
+ const videoEngine = new webllm.MLCEngine();
26
+ videoEngine.setInitProgressCallback(videoUpdateEngineInitProgressCallback);
27
+
28
+ let videoIsGenerating = false; // Flag to prevent multiple generations
29
+
30
+ async function videoInitializeWebLLMEngine() {
31
+ logMessage("Model initialization started.", "system");
32
+ document.getElementById("video-loading-spinner").classList.remove("hidden"); // Show spinner
33
+ videoSelectedModel = document.getElementById("video-model-selection").value;
34
+ const config = {
35
+ temperature: 0.7, // Adjusted for more precise answers
36
+ top_p: 0.9
37
+ };
38
+ try {
39
+ await videoEngine.reload(videoSelectedModel, config);
40
+ document.getElementById("video-selected-model").textContent = videoSelectedModel;
41
+ document.getElementById("video-start_button").disabled = false;
42
+ document.getElementById("video-text-input").disabled = false; // Enable text input after initialization
43
+ document.getElementById("video-submit-button").disabled = false; // Enable submit button after initialization
44
+ document.getElementById("video-speech-controls").disabled = false; // Enable speech controls after initialization
45
+ document.getElementById("video-configuration").classList.remove("hidden");
46
+ logMessage("Model initialized successfully.", "system");
47
+ } catch (error) {
48
+ console.error("Error initializing the model:", error);
49
+ alert("Failed to initialize the model. Please try again.");
50
+ logMessage("Failed to initialize the model.", "error");
51
+ } finally {
52
+ document.getElementById("video-loading-spinner").classList.add("hidden"); // Hide spinner
53
+ }
54
+ }
55
+
56
+ async function videoStreamingGenerating(messages, onUpdate, onFinish, onError) {
57
+ if (videoIsGenerating) {
58
+ console.warn("Video Generation already in progress.");
59
+ return;
60
+ }
61
+ videoIsGenerating = true;
62
+ try {
63
+ let curMessage = "";
64
+ const completion = await videoEngine.chat.completions.create({
65
+ stream: true,
66
+ messages
67
+ });
68
+ for await (const chunk of completion) {
69
+ const curDelta = chunk.choices[0].delta.content;
70
+ if (curDelta) {
71
+ curMessage += curDelta;
72
+ }
73
+ onUpdate(curMessage);
74
+ }
75
+ const finalMessage = await videoEngine.getMessage();
76
+ console.log(`Digital Human Video Generated final message: ${finalMessage}`); // Debugging
77
+ onFinish(finalMessage);
78
+ logMessage("Response generated successfully.", "system");
79
+ } catch (err) {
80
+ console.error(err);
81
+ onError(err);
82
+ logMessage("An error occurred during response generation.", "error");
83
+ } finally {
84
+ videoIsGenerating = false;
85
+ }
86
+ }
87
+
88
+ // Flag to track the last input method
89
+ let videoLastInputWasVoice = false;
90
+
91
+ function videoAppendMessage(message) {
92
+ console.log(`Digital Human Video Appending message: ${message.content} (Role: ${message.role})`); // Debugging
93
+ const videoChatBox = document.getElementById("video-chat-box");
94
+
95
+ // Check if the assistant's message is already appended to avoid duplication
96
+ if (message.role === "assistant") {
97
+ const existingMessages = videoChatBox.querySelectorAll(".message");
98
+ const lastMessage = existingMessages[existingMessages.length - 1];
99
+ if (lastMessage && lastMessage.textContent === message.content) {
100
+ console.warn("Duplicate assistant message detected in Video section, skipping append.");
101
+
102
+ // Only trigger TTS for assistant messages if the last input was via voice
103
+ if (message.role === "assistant" && message.content !== "typing..." && videoLastInputWasVoice) {
104
+ videoSpeak(message.content);
105
+ }
106
+
107
+ return; // Exit to avoid appending the same message twice
108
+ }
109
+ }
110
+
111
+ const container = document.createElement("div");
112
+ container.classList.add("message-container");
113
+ const newMessage = document.createElement("div");
114
+ newMessage.classList.add("message");
115
+ newMessage.textContent = message.content;
116
+
117
+ if (message.role === "user") {
118
+ container.classList.add("user");
119
+ } else {
120
+ container.classList.add("assistant");
121
+ }
122
+
123
+ container.appendChild(newMessage);
124
+ videoChatBox.appendChild(container);
125
+ videoChatBox.scrollTop = videoChatBox.scrollHeight;
126
+
127
+ // Only trigger TTS for assistant messages if the last input was via voice
128
+ if (message.role === "assistant" && message.content !== "typing..." && videoLastInputWasVoice) {
129
+ videoSpeak(message.content);
130
+ }
131
+ }
132
+
133
+ function videoUpdateLastMessage(content) {
134
+ const messageDoms = document.getElementById("video-chat-box").querySelectorAll(".message");
135
+ const lastMessageDom = messageDoms[messageDoms.length - 1];
136
+ lastMessageDom.textContent = content;
137
+ }
138
+
139
+ function videoOnSpeechRecognized(transcript) {
140
+ const input = transcript.trim();
141
+ const message = {
142
+ content: input,
143
+ role: "user"
144
+ };
145
+ if (input.length === 0) {
146
+ return;
147
+ }
148
+ videoLastInputWasVoice = true; // Set flag as voice input
149
+ console.log(`Digital Human Video Voice input received: ${input}`); // Debugging
150
+ document.getElementById("video-start_button").disabled = true;
151
+ document.getElementById("video-submit-button").disabled = true; // Disable submit button during processing
152
+
153
+ videoMessages.push(message);
154
+ videoAppendMessage(message);
155
+ logMessage(`User (Voice): ${input}`, "user");
156
+
157
+ // Append "typing..." placeholder
158
+ const aiPlaceholder = {
159
+ content: "typing...",
160
+ role: "assistant"
161
+ };
162
+ videoAppendMessage(aiPlaceholder);
163
+ logMessage("VideoBot is typing...", "system");
164
+
165
+ const onFinishGenerating = (finalMessage) => {
166
+ console.log(`Digital Human Video Finishing generation with message: ${finalMessage}`); // Debugging
167
+ // Remove the "typing..." placeholder
168
+ const videoChatBox = document.getElementById("video-chat-box");
169
+ const lastMessageContainer = videoChatBox.lastElementChild;
170
+ if (lastMessageContainer && lastMessageContainer.querySelector(".message").textContent === "typing...") {
171
+ videoChatBox.removeChild(lastMessageContainer);
172
+ }
173
+
174
+ // Append the final message
175
+ const aiMessage = {
176
+ content: finalMessage,
177
+ role: "assistant"
178
+ };
179
+ videoAppendMessage(aiMessage);
180
+ logMessage(`VideoBot: ${finalMessage}`, "assistant");
181
+
182
+ document.getElementById("video-start_button").disabled = false;
183
+ document.getElementById("video-submit-button").disabled = false; // Re-enable submit button after processing
184
+ videoEngine.runtimeStatsText().then((statsText) => {
185
+ document.getElementById("video-chat-stats").classList.remove("hidden");
186
+ document.getElementById("video-chat-stats").textContent = statsText;
187
+ logMessage(`Runtime Stats: ${statsText}`, "system");
188
+ });
189
+ };
190
+
191
+ videoStreamingGenerating(
192
+ videoMessages,
193
+ videoUpdateLastMessage,
194
+ onFinishGenerating,
195
+ (err) => {
196
+ console.error(err);
197
+ alert("An error occurred while generating the response. Please try again.");
198
+ logMessage("Error during response generation.", "error");
199
+ document.getElementById("video-start_button").disabled = false;
200
+ document.getElementById("video-submit-button").disabled = false;
201
+ }
202
+ );
203
+ }
204
+
205
+ // Speech Recognition Code for Video
206
+ let videoRecognizing = false;
207
+ let videoIgnore_onend;
208
+ let videoFinal_transcript = '';
209
+ let videoRecognition;
210
+
211
+ function videoStartButton(event) {
212
+ if (videoRecognizing) {
213
+ videoRecognition.stop();
214
+ return;
215
+ }
216
+ videoFinal_transcript = '';
217
+ videoRecognition.lang = 'en-US';
218
+ videoRecognition.start();
219
+ videoIgnore_onend = false;
220
+ document.getElementById("video-start_button").classList.add("mic-animate");
221
+ logMessage("Voice input started.", "system");
222
+ }
223
+
224
+ if (!('webkitSpeechRecognition' in window)) {
225
+ alert("Web Speech API is not supported by this browser.");
226
+ logMessage("Web Speech API is not supported by this browser.", "error");
227
+ } else {
228
+ videoRecognition = new webkitSpeechRecognition();
229
+ videoRecognition.continuous = false; // Non-continuous recognition
230
+ videoRecognition.interimResults = false; // Get only final results
231
+
232
+ videoRecognition.onstart = function() {
233
+ videoRecognizing = true;
234
+ logMessage("Speech recognition started.", "system");
235
+ };
236
+
237
+ videoRecognition.onerror = function(event) {
238
+ if (event.error == 'no-speech') {
239
+ document.getElementById("video-start_button").classList.remove("mic-animate");
240
+ alert('No speech was detected in Video section.');
241
+ logMessage("No speech detected.", "error");
242
+ videoIgnore_onend = true;
243
+ }
244
+ if (event.error == 'audio-capture') {
245
+ document.getElementById("video-start_button").classList.remove("mic-animate");
246
+ alert('No microphone was found in Video section.');
247
+ logMessage("No microphone found.", "error");
248
+ videoIgnore_onend = true;
249
+ }
250
+ if (event.error == 'not-allowed') {
251
+ alert('Permission to use microphone was denied in Video section.');
252
+ logMessage("Microphone permission denied.", "error");
253
+ videoIgnore_onend = true;
254
+ }
255
+ };
256
+
257
+ videoRecognition.onend = function() {
258
+ videoRecognizing = false;
259
+ document.getElementById("video-start_button").classList.remove("mic-animate");
260
+ logMessage("Speech recognition ended.", "system");
261
+ if (videoIgnore_onend) {
262
+ return;
263
+ }
264
+ if (!videoFinal_transcript) {
265
+ logMessage("No transcript captured.", "error");
266
+ return;
267
+ }
268
+ // Process the final transcript
269
+ videoOnSpeechRecognized(videoFinal_transcript);
270
+ };
271
+
272
+ videoRecognition.onresult = function(event) {
273
+ for (let i = event.resultIndex; i < event.results.length; ++i) {
274
+ if (event.results[i].isFinal) {
275
+ videoFinal_transcript += event.results[i][0].transcript;
276
+ }
277
+ }
278
+ videoFinal_transcript = videoFinal_transcript.trim();
279
+ logMessage(`Recognized Speech: ${videoFinal_transcript}`, "user");
280
+ };
281
+ }
282
+
283
+ document.getElementById("video-start_button").addEventListener("click", function(event) {
284
+ videoStartButton(event);
285
+ });
286
+
287
+ // Initialize Model Selection
288
+ videoAvailableModels.forEach((modelId) => {
289
+ const option = document.createElement("option");
290
+ option.value = modelId;
291
+ option.textContent = modelId;
292
+ document.getElementById("video-model-selection").appendChild(option);
293
+ });
294
+ document.getElementById("video-model-selection").value = videoSelectedModel;
295
+
296
+ // **Enable the Download Model button after models are loaded**
297
+ document.getElementById("video-download").disabled = false;
298
+
299
+ document.getElementById("video-download").addEventListener("click", function () {
300
+ videoInitializeWebLLMEngine().then(() => {
301
+ document.getElementById("video-start_button").disabled = false;
302
+ // Enable speech controls after model initialization
303
+ document.getElementById("video-speech-rate").disabled = false;
304
+ document.getElementById("video-speech-pitch").disabled = false;
305
+ logMessage("Model download initiated.", "system");
306
+ });
307
+ });
308
+
309
+ document.getElementById("video-clear-logs").addEventListener("click", function () {
310
+ document.getElementById("video-logs").innerHTML = '';
311
+ logMessage("Logs cleared.", "system");
312
+ });
313
+
314
+ // ===== TTS Integration =====
315
+
316
+ // Initialize Speech Synthesis
317
+ let videoSpeech = new SpeechSynthesisUtterance();
318
+ videoSpeech.lang = "en";
319
+
320
+ let videoVoices = [];
321
+
322
+ // Use addEventListener instead of directly assigning to onvoiceschanged
323
+ window.speechSynthesis.addEventListener("voiceschanged", () => {
324
+ videoVoices = window.speechSynthesis.getVoices();
325
+ videoPopulateVoices();
326
+ });
327
+
328
+ function videoPopulateVoices() {
329
+ const voiceSelect = document.getElementById("video-tools");
330
+ voiceSelect.innerHTML = ''; // Clear existing options
331
+ videoVoices.forEach((voice, i) => {
332
+ const option = new Option(voice.name, i);
333
+ voiceSelect.appendChild(option);
334
+ });
335
+ if (videoVoices.length > 0) {
336
+ const savedVoice = localStorage.getItem("video-selectedVoice");
337
+ if (savedVoice !== null && videoVoices[savedVoice]) {
338
+ videoSpeech.voice = videoVoices[savedVoice];
339
+ voiceSelect.value = savedVoice;
340
+ } else {
341
+ videoSpeech.voice = videoVoices[0];
342
+ }
343
+ }
344
+ }
345
+
346
+ // Voice Selection Event Listener
347
+ document.getElementById("video-tools").addEventListener("change", () => {
348
+ const selectedVoiceIndex = document.getElementById("video-tools").value;
349
+ videoSpeech.voice = videoVoices[selectedVoiceIndex];
350
+ // Save to localStorage
351
+ localStorage.setItem("video-selectedVoice", selectedVoiceIndex);
352
+ logMessage(`Voice changed to: ${videoVoices[selectedVoiceIndex].name}`, "system");
353
+ });
354
+
355
+ // Function to Speak Text with Voice Selection and Handling Large Texts
356
+ function videoSpeak(text) {
357
+ if (!window.speechSynthesis) {
358
+ console.warn("Speech Synthesis not supported in this browser for Video section.");
359
+ logMessage("Speech Synthesis not supported in this browser.", "error");
360
+ return;
361
+ }
362
+
363
+ // Show spinner and enable Stop button
364
+ document.getElementById("video-loading-spinner").classList.remove("hidden");
365
+ document.getElementById("video-stop_button").disabled = false;
366
+ logMessage("TTS started.", "system");
367
+
368
+ // Retrieve the currently selected voice
369
+ const selectedVoice = videoSpeech.voice;
370
+
371
+ // Split the text into sentences to manage large texts
372
+ const sentences = text.match(/[^\.!\?]+[\.!\?]+/g) || [text];
373
+
374
+ let utterancesCount = sentences.length;
375
+
376
+ sentences.forEach(sentence => {
377
+ const utterance = new SpeechSynthesisUtterance(sentence.trim());
378
+
379
+ // Assign the selected voice to the utterance
380
+ if (selectedVoice) {
381
+ utterance.voice = selectedVoice;
382
+ }
383
+
384
+ // Assign rate and pitch from sliders
385
+ const rate = parseFloat(document.getElementById("video-speech-rate").value);
386
+ const pitch = parseFloat(document.getElementById("video-speech-pitch").value);
387
+ utterance.rate = rate; // Adjust the speaking rate (0.1 to 10)
388
+ utterance.pitch = pitch; // Adjust the pitch (0 to 2)
389
+
390
+ // Add event listeners for debugging or additional functionality
391
+ utterance.onstart = () => {
392
+ console.log("Speech started:", sentence);
393
+ logMessage(`TTS started: ${sentence.trim()}`, "system");
394
+ };
395
+
396
+ utterance.onend = () => {
397
+ console.log("Speech ended:", sentence);
398
+ logMessage(`TTS ended: ${sentence.trim()}`, "system");
399
+ utterancesCount--;
400
+ if (utterancesCount === 0) {
401
+ // Hide spinner and disable Stop button when all utterances have been spoken
402
+ document.getElementById("video-loading-spinner").classList.add("hidden");
403
+ document.getElementById("video-stop_button").disabled = true;
404
+ logMessage("All TTS messages have been spoken.", "system");
405
+ }
406
+ };
407
+
408
+ utterance.onerror = (e) => {
409
+ console.error("Speech Synthesis Error:", e);
410
+ alert("An error occurred during speech synthesis. Please try again.");
411
+ logMessage("Speech synthesis encountered an error.", "error");
412
+ utterancesCount = 0;
413
+ document.getElementById("video-loading-spinner").classList.add("hidden");
414
+ document.getElementById("video-stop_button").disabled = true;
415
+ };
416
+
417
+ window.speechSynthesis.speak(utterance);
418
+ });
419
+ }
420
+
421
+ // ===== New: Stop Speech Functionality =====
422
+
423
+ /**
424
+ * Stops any ongoing speech synthesis.
425
+ */
426
+ function videoStopSpeech() {
427
+ if (window.speechSynthesis.speaking) {
428
+ window.speechSynthesis.cancel();
429
+ document.getElementById("video-loading-spinner").classList.add("hidden");
430
+ document.getElementById("video-stop_button").disabled = true;
431
+ logMessage("Speech synthesis stopped by user.", "system");
432
+ }
433
+ }
434
+
435
+ // Event Listener for Stop Button
436
+ document.getElementById("video-stop_button").addEventListener("click", function () {
437
+ videoStopSpeech();
438
+ });
439
+
440
+ // ===== New: Text Input Handling =====
441
+
442
+ // Function to Handle Text Submission
443
+ function videoHandleTextSubmit() {
444
+ const textInput = document.getElementById("video-text-input");
445
+ const input = textInput.value.trim();
446
+ if (input.length === 0) {
447
+ return;
448
+ }
449
+ textInput.value = ''; // Clear the input field
450
+ const message = {
451
+ content: input,
452
+ role: "user" // Ensure this is correctly set
453
+ };
454
+ console.log(`Digital Human Video Text input received: ${input}`); // Debugging
455
+ logMessage(`User: ${input}`, "user");
456
+
457
+ videoLastInputWasVoice = false; // Set flag as text input
458
+ document.getElementById("video-submit-button").disabled = true; // Disable to prevent multiple submissions
459
+
460
+ videoMessages.push(message);
461
+ videoAppendMessage(message);
462
+
463
+ // Append "typing..." placeholder
464
+ const aiPlaceholder = {
465
+ content: "typing...",
466
+ role: "assistant"
467
+ };
468
+ videoAppendMessage(aiPlaceholder);
469
+ logMessage("VideoBot is typing...", "system");
470
+
471
+ const onFinishGenerating = (finalMessage) => {
472
+ console.log(`Digital Human Video Finishing generation with message: ${finalMessage}`); // Debugging
473
+ // Remove the "typing..." placeholder
474
+ const videoChatBox = document.getElementById("video-chat-box");
475
+ const lastMessageContainer = videoChatBox.lastElementChild;
476
+ if (lastMessageContainer && lastMessageContainer.querySelector(".message").textContent === "typing...") {
477
+ videoChatBox.removeChild(lastMessageContainer);
478
+ }
479
+
480
+ // Append the final message
481
+ const aiMessage = {
482
+ content: finalMessage,
483
+ role: "assistant"
484
+ };
485
+ videoAppendMessage(aiMessage);
486
+ logMessage(`VideoBot: ${finalMessage}`, "assistant");
487
+
488
+ // Trigger TTS for assistant messages if required
489
+ if (videoLastInputWasVoice) {
490
+ videoSpeak(finalMessage);
491
+ }
492
+
493
+ document.getElementById("video-submit-button").disabled = false; // Re-enable submit button after processing
494
+ videoEngine.runtimeStatsText().then((statsText) => {
495
+ document.getElementById("video-chat-stats").classList.remove("hidden");
496
+ document.getElementById("video-chat-stats").textContent = statsText;
497
+ logMessage(`Runtime Stats: ${statsText}`, "system");
498
+ });
499
+ };
500
+
501
+ videoStreamingGenerating(
502
+ videoMessages,
503
+ videoUpdateLastMessage,
504
+ onFinishGenerating,
505
+ (err) => {
506
+ console.error(err);
507
+ alert("An error occurred while generating the response. Please try again.");
508
+ logMessage("Error during response generation.", "error");
509
+ document.getElementById("video-submit-button").disabled = false;
510
+ }
511
+ );
512
+ }
513
+
514
+ // Event Listener for Submit Button
515
+ document.getElementById("video-submit-button").addEventListener("click", function () {
516
+ videoHandleTextSubmit();
517
+ });
518
+
519
+ // Event Listener for Enter Key in Text Input
520
+ document.getElementById("video-text-input").addEventListener("keypress", function (e) {
521
+ if (e.key === 'Enter') {
522
+ videoHandleTextSubmit();
523
+ }
524
+ });
525
+
526
+ // ===== Persisting User Preferences =====
527
+
528
+ // Load Preferences on Initialization
529
+ window.addEventListener("load", () => {
530
+ const savedVoice = localStorage.getItem("video-selectedVoice");
531
+ if (savedVoice !== null && videoVoices[savedVoice]) {
532
+ document.getElementById("video-tools").value = savedVoice;
533
+ videoSpeech.voice = videoVoices[savedVoice];
534
+ logMessage(`Loaded saved voice: ${videoVoices[savedVoice].name}`, "system");
535
+ }
536
+
537
+ const savedRate = localStorage.getItem("video-speechRate");
538
+ if (savedRate !== null) {
539
+ document.getElementById("video-speech-rate").value = savedRate;
540
+ videoSpeech.rate = parseFloat(savedRate);
541
+ logMessage(`Loaded saved speech rate: ${savedRate}`, "system");
542
+ }
543
+
544
+ const savedPitch = localStorage.getItem("video-speechPitch");
545
+ if (savedPitch !== null) {
546
+ document.getElementById("video-speech-pitch").value = savedPitch;
547
+ videoSpeech.pitch = parseFloat(savedPitch);
548
+ logMessage(`Loaded saved speech pitch: ${savedPitch}`, "system");
549
+ }
550
+ });
551
+
552
+ // Save Speech Rate
553
+ document.getElementById("video-speech-rate").addEventListener("input", (e) => {
554
+ const rate = e.target.value;
555
+ videoSpeech.rate = parseFloat(rate);
556
+ localStorage.setItem("video-speechRate", rate);
557
+ logMessage(`Speech rate changed to: ${rate}`, "system");
558
+ });
559
+
560
+ // Save Speech Pitch
561
+ document.getElementById("video-speech-pitch").addEventListener("input", (e) => {
562
+ const pitch = e.target.value;
563
+ videoSpeech.pitch = parseFloat(pitch);
564
+ localStorage.setItem("video-speechPitch", pitch);
565
+ logMessage(`Speech pitch changed to: ${pitch}`, "system");
566
+ });
567
+
568
+ // ===== Logging Function =====
569
+
570
+ /**
571
+ * Logs messages to the #video-logs container.
572
+ * @param {string} message - The message to log.
573
+ * @param {string} type - The type of message: 'user', 'assistant', 'system', 'error'.
574
+ */
575
+ function logMessage(message, type) {
576
+ const videoLogs = document.getElementById("video-logs");
577
+ const logEntry = document.createElement("div");
578
+ logEntry.classList.add("log-entry");
579
+ logEntry.textContent = `[${type.toUpperCase()}] ${message}`;
580
+
581
+ // Style log entries based on type
582
+ switch(type) {
583
+ case 'user':
584
+ logEntry.style.color = "#00796B";
585
+ break;
586
+ case 'assistant':
587
+ logEntry.style.color = "#004D40";
588
+ break;
589
+ case 'system':
590
+ logEntry.style.color = "#555555";
591
+ break;
592
+ case 'error':
593
+ logEntry.style.color = "#E53935";
594
+ break;
595
+ default:
596
+ logEntry.style.color = "#000000";
597
+ }
598
+
599
+ videoLogs.appendChild(logEntry);
600
+ videoLogs.scrollTop = videoLogs.scrollHeight;
601
+ }
602
+
603
+ // ===== TTS Integration Continued =====
604
+
605
+ // Optional: Global Listener to Detect When All Speech Has Finished
606
+ window.speechSynthesis.addEventListener('end', () => {
607
+ console.log("All video speech has been spoken.");
608
+ logMessage("All TTS messages have been spoken.", "system");
609
+ // Ensure Stop button is disabled after speech ends
610
+ document.getElementById("video-stop_button").disabled = true;
611
+ });
612
+ });
finalwork4.html ADDED
@@ -0,0 +1,562 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>Aged Guru</title>
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <!-- Google Fonts for better typography -->
8
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&family=Roboto:wght@400;500&display=swap" rel="stylesheet">
9
+ <!-- Include necessary CSS styles -->
10
+ <link rel="stylesheet" href="base.css">
11
+ <link rel="stylesheet" href="coder.css">
12
+ <link rel="stylesheet" href="digital.human.video.css">
13
+ <link rel="stylesheet" href="digital.human.audio.css">
14
+ <link rel="stylesheet" href="advanced.math.css">
15
+ <link rel="stylesheet" href="interdisciplinary.css"> <!-- New CSS for Interdisciplinary -->
16
+ </head>
17
+ <body>
18
+
19
+ <!-- Sidebar -->
20
+ <div class="sidebar">
21
+ <h2>AGED GURU</h2>
22
+ <a href="#" class="active" data-content="voice">Digital Human Voice</a>
23
+ <a href="#" data-content="video">Digital Human Video</a>
24
+ <a href="#" data-content="coder">Young Coder</a>
25
+ <a href="#" data-content="advanced-math">Advanced Mathematics & Problem Solving</a>
26
+ <a href="#" data-content="interdisciplinary">Interdisciplinary Studies Assistant</a>
27
+ <a href="#" data-content="insights">Insights</a>
28
+ <a href="#" data-content="podcast">Podcast</a>
29
+ <a href="#" data-content="3d-explorer">3D Explorer</a>
30
+ <a href="#" data-content="knowledge-base">Knowledge Base</a>
31
+ <a href="#" data-content="digital-twin">Digital Twin</a>
32
+ </div>
33
+
34
+ <!-- Main Content -->
35
+ <div class="main-content" id="main-content">
36
+ <div class="header">
37
+ <h1>5-MIN SCIENTIST PORTAL</h1>
38
+ <!-- You can add user profile or settings here -->
39
+ </div>
40
+ <!-- Content will be loaded here -->
41
+ <div id="content-area">
42
+
43
+
44
+ <!-- -----------------Digital Human Voice Content Starts---------------- -->
45
+ <div id="voice" class="content-section">
46
+ <div class="card voice-card">
47
+ <!-- Digital Human Voice Layout -->
48
+ <div class="voice-options">
49
+ <!-- Model Selection Section -->
50
+ <div id="voice-model-selection-container" class="option-section">
51
+ <label for="voice-model-selection">Select Model:</label>
52
+ <select id="voice-model-selection"></select>
53
+ <button id="voice-download" disabled>Download Model</button>
54
+ </div>
55
+
56
+ <!-- Voice Selection Section -->
57
+ <div id="voice-tool-selection-container" class="option-section">
58
+ <label for="voice-tools">Select Voice:</label>
59
+ <select id="voice-tools"></select>
60
+ </div>
61
+
62
+ <!-- Speech Controls Section -->
63
+ <div id="voice-speech-controls" class="option-section">
64
+ <label for="voice-speech-rate">Speech Rate:</label>
65
+ <input type="range" id="voice-speech-rate" min="0.5" max="2" step="0.1" value="1" disabled>
66
+
67
+ <label for="voice-speech-pitch">Speech Pitch:</label>
68
+ <input type="range" id="voice-speech-pitch" min="0" max="2" step="0.1" value="1" disabled>
69
+ </div>
70
+
71
+ <!-- Logs Section -->
72
+ <div id="voice-logs-container" class="option-section">
73
+ <h3>Logs</h3>
74
+ <div id="voice-logs">
75
+ <!-- Logs will appear here -->
76
+ </div>
77
+ <button id="voice-clear-logs">Clear Logs</button>
78
+ </div>
79
+ </div>
80
+
81
+ <div class="chat-window">
82
+ <!-- Chat Box Section -->
83
+ <div id="voice-chat-container">
84
+ <div id="voice-chat-box">
85
+ <!-- Chat messages will appear here -->
86
+ </div>
87
+
88
+ <!-- Chat Stats -->
89
+ <div id="voice-chat-stats" class="hidden">Runtime Stats: N/A</div>
90
+
91
+ <!-- Chat Input Section -->
92
+ <div id="voice-text-input-container">
93
+ <input type="text" id="voice-text-input" placeholder="Type your message here..." disabled>
94
+ <button id="voice-submit-button" disabled>Send</button>
95
+ </div>
96
+
97
+ <!-- Microphone Controls -->
98
+ <div id="voice-mic-container">
99
+ <button id="voice-start_button" aria-label="Start Voice Input" disabled>
100
+ <!-- Mic Icon SVG -->
101
+ <svg class="mic-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
102
+ <path d="M12 14a3 3 0 003-3V5a3 3 0 00-6 0v6a3 3 0 003 3zm5-3a5 5 0 01-10 0H5a7 7 0 0014 0h-2z"/>
103
+ </svg>
104
+ </button>
105
+ <button id="voice-stop_button" aria-label="Stop Speech" disabled>
106
+ <!-- Stop Icon SVG -->
107
+ <svg class="stop-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
108
+ <rect x="6" y="6" width="12" height="12" rx="2" ry="2"></rect>
109
+ </svg>
110
+ </button>
111
+ </div>
112
+ </div>
113
+
114
+ <!-- Loading Spinner -->
115
+ <div id="voice-loading-spinner" class="hidden">
116
+ <div class="spinner"></div>
117
+ </div>
118
+
119
+ <!-- Configuration Info -->
120
+ <div id="voice-configuration" class="hidden">
121
+ <p>Model Initialized: <span id="voice-selected-model">N/A</span></p>
122
+ </div>
123
+ </div>
124
+ </div>
125
+ </div>
126
+ <!-- -----------------Digital Human Voice Content Ends---------------- -->
127
+
128
+
129
+
130
+
131
+ <!-- --------------------Digital Human Mentor Video Content Starts---------------- -->
132
+ <div id="video" class="content-section hidden">
133
+ <div class="card video-card">
134
+ <!-- Digital Human Video Assistant Layout -->
135
+ <div class="video-options">
136
+ <!-- Model Selection Section -->
137
+ <div id="video-model-selection-container" class="option-section">
138
+ <label for="video-model-selection">Select Model:</label>
139
+ <select id="video-model-selection"></select>
140
+ <button id="video-download" disabled>Download Model</button>
141
+ </div>
142
+
143
+ <!-- Voice Selection Section -->
144
+ <div id="video-tool-selection-container" class="option-section">
145
+ <label for="video-tools">Select Voice:</label>
146
+ <select id="video-tools"></select>
147
+ </div>
148
+
149
+ <!-- Speech Controls Section -->
150
+ <div id="video-speech-controls" class="option-section">
151
+ <label for="video-speech-rate">Speech Rate:</label>
152
+ <input type="range" id="video-speech-rate" min="0.5" max="2" step="0.1" value="1" disabled>
153
+
154
+ <label for="video-speech-pitch">Speech Pitch:</label>
155
+ <input type="range" id="video-speech-pitch" min="0" max="2" step="0.1" value="1" disabled>
156
+ </div>
157
+
158
+ <!-- Logs Section -->
159
+ <div id="video-logs-container" class="option-section">
160
+ <h3>Logs</h3>
161
+ <div id="video-logs">
162
+ <!-- Logs will appear here -->
163
+ </div>
164
+ <button id="video-clear-logs">Clear Logs</button>
165
+ </div>
166
+ </div>
167
+
168
+ <div class="chat-window">
169
+ <!-- Chat Box Section -->
170
+ <div id="video-chat-container">
171
+ <div id="video-chat-box">
172
+ <!-- Chat messages will appear here -->
173
+ </div>
174
+
175
+ <!-- Chat Stats -->
176
+ <div id="video-chat-stats" class="hidden">Runtime Stats: N/A</div>
177
+
178
+ <!-- Chat Input Section -->
179
+ <div id="video-text-input-container">
180
+ <input type="text" id="video-text-input" placeholder="Type your message here..." disabled>
181
+ <button id="video-submit-button" disabled>Send</button>
182
+ </div>
183
+
184
+ <!-- Microphone Controls -->
185
+ <div id="video-mic-container">
186
+ <button id="video-start_button" aria-label="Start Voice Input" disabled>
187
+ <!-- Mic Icon SVG -->
188
+ <svg class="mic-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
189
+ <path d="M12 14a3 3 0 003-3V5a3 3 0 00-6 0v6a3 3 0 003 3zm5-3a5 5 0 01-10 0H5a7 7 0 0014 0h-2z"/>
190
+ </svg>
191
+ </button>
192
+ <button id="video-stop_button" aria-label="Stop Speech" disabled>
193
+ <!-- Stop Icon SVG -->
194
+ <svg class="stop-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
195
+ <rect x="6" y="6" width="12" height="12" rx="2" ry="2"></rect>
196
+ </svg>
197
+ </button>
198
+ </div>
199
+ </div>
200
+
201
+ <!-- Loading Spinner -->
202
+ <div id="video-loading-spinner" class="hidden">
203
+ <div class="spinner"></div>
204
+ </div>
205
+
206
+ <!-- Configuration Info -->
207
+ <div id="video-configuration" class="hidden">
208
+ <p>Model Initialized: <span id="video-selected-model">N/A</span></p>
209
+ </div>
210
+ </div>
211
+ </div>
212
+ </div>
213
+ <!-- --------------------Digital Human Mentor Video Content Ends----------------- -->
214
+
215
+
216
+
217
+
218
+
219
+ <!-- --------------------Coder Content Starts------------------------------------- -->
220
+ <div id="coder" class="content-section hidden">
221
+ <div class="card coder-card">
222
+ <!-- Young Coder Layout -->
223
+ <div class="coder-options">
224
+ <!-- Model Selection Section -->
225
+ <div id="coder-model-selection-container" class="option-section">
226
+ <label for="coder-model-selection">Select Model:</label>
227
+ <select id="coder-model-selection"></select>
228
+ <button id="coder-download" disabled>Download Model</button>
229
+ </div>
230
+
231
+ <!-- Tool Selection Section -->
232
+ <div id="coder-tool-selection-container" class="option-section">
233
+ <label for="coder-tools">Select Voice:</label>
234
+ <select id="coder-tools"></select>
235
+ </div>
236
+
237
+ <!-- Speech Controls Section -->
238
+ <div id="coder-speech-controls" class="option-section">
239
+ <label for="coder-speech-rate">Speech Rate:</label>
240
+ <input type="range" id="coder-speech-rate" min="0.5" max="2" step="0.1" value="1" disabled>
241
+
242
+ <label for="coder-speech-pitch">Speech Pitch:</label>
243
+ <input type="range" id="coder-speech-pitch" min="0" max="2" step="0.1" value="1" disabled>
244
+ </div>
245
+
246
+ <!-- Logs Section -->
247
+ <div id="coder-logs-container" class="option-section">
248
+ <h3>Logs</h3>
249
+ <div id="coder-logs">
250
+ <!-- Logs will appear here -->
251
+ </div>
252
+ <button id="coder-clear-logs">Clear Logs</button>
253
+ </div>
254
+ </div>
255
+
256
+ <div class="chat-window">
257
+ <!-- Chat Box Section -->
258
+ <div id="coder-chat-container">
259
+ <div id="coder-chat-box">
260
+ <!-- Chat messages will appear here -->
261
+ </div>
262
+
263
+ <!-- Chat Stats -->
264
+ <div id="coder-chat-stats" class="hidden">Runtime Stats: N/A</div>
265
+
266
+ <!-- Chat Input Section -->
267
+ <div id="coder-text-input-container">
268
+ <input type="text" id="coder-text-input" placeholder="Type your message here..." disabled>
269
+ <button id="coder-submit-button" disabled>Send</button>
270
+ </div>
271
+
272
+ <!-- Microphone Controls -->
273
+ <div id="coder-mic-container">
274
+ <button id="coder-start_button" aria-label="Start Voice Input" disabled>
275
+ <!-- Mic Icon SVG -->
276
+ <svg class="mic-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
277
+ <path d="M12 14a3 3 0 003-3V5a3 3 0 00-6 0v6a3 3 0 003 3zm5-3a5 5 0 01-10 0H5a7 7 0 0014 0h-2z"/>
278
+ </svg>
279
+ </button>
280
+ <button id="coder-stop_button" aria-label="Stop Speech" disabled>
281
+ <!-- Stop Icon SVG -->
282
+ <svg class="stop-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
283
+ <rect x="6" y="6" width="12" height="12" rx="2" ry="2"></rect>
284
+ </svg>
285
+ </button>
286
+ </div>
287
+ </div>
288
+
289
+ <!-- Loading Spinner -->
290
+ <div id="coder-loading-spinner" class="hidden">
291
+ <div class="spinner"></div>
292
+ </div>
293
+
294
+ <!-- Configuration Info -->
295
+ <div id="coder-configuration" class="hidden">
296
+ <p>Model Initialized: <span id="coder-selected-model">N/A</span></p>
297
+ </div>
298
+ </div>
299
+ </div>
300
+ </div>
301
+ <!-- --------------------Coder Content Ends---------------------------------------- -->
302
+
303
+
304
+
305
+ <!-- --------------------Math Content Starts--------------------------------------- -->
306
+ <div id="advanced-math" class="content-section hidden">
307
+ <div class="card advanced-math-card">
308
+ <!-- Advanced Mathematics & Problem Solving Layout -->
309
+ <div class="math-options">
310
+ <!-- Model Selection Section -->
311
+ <div id="math-model-selection-container" class="option-section">
312
+ <label for="math-model-selection">Select Math Model:</label>
313
+ <select id="math-model-selection"></select>
314
+ <button id="math-download" disabled>Download Model</button>
315
+ </div>
316
+
317
+ <!-- Tool Selection Section -->
318
+ <div id="math-tool-selection-container" class="option-section">
319
+ <label for="math-tools">Select Voice:</label>
320
+ <select id="math-tools"></select>
321
+ </div>
322
+
323
+ <!-- Speech Controls Section -->
324
+ <div id="math-speech-controls" class="option-section">
325
+ <label for="math-speech-rate">Speech Rate:</label>
326
+ <input type="range" id="math-speech-rate" min="0.5" max="2" step="0.1" value="1" disabled>
327
+
328
+ <label for="math-speech-pitch">Speech Pitch:</label>
329
+ <input type="range" id="math-speech-pitch" min="0" max="2" step="0.1" value="1" disabled>
330
+ </div>
331
+
332
+ <!-- Logs Section -->
333
+ <div id="math-logs-container" class="option-section">
334
+ <h3>Logs</h3>
335
+ <div id="math-logs">
336
+ <!-- Logs will appear here -->
337
+ </div>
338
+ <button id="math-clear-logs">Clear Logs</button>
339
+ </div>
340
+ </div>
341
+
342
+ <div class="chat-window">
343
+ <!-- Chat Box Section -->
344
+ <div id="math-chat-container">
345
+ <div id="math-chat-box">
346
+ <!-- Chat messages will appear here -->
347
+ </div>
348
+
349
+ <!-- Chat Stats -->
350
+ <div id="math-chat-stats" class="hidden">Runtime Stats: N/A</div>
351
+
352
+ <!-- Chat Input Section -->
353
+ <div id="math-text-input-container">
354
+ <input type="text" id="math-text-input" placeholder="Type your math problem here..." disabled>
355
+ <button id="math-submit-button" disabled>Submit</button>
356
+ </div>
357
+
358
+ <!-- Microphone Controls -->
359
+ <div id="math-mic-container">
360
+ <button id="math-start_button" aria-label="Start Voice Input" disabled>
361
+ <!-- Mic Icon SVG -->
362
+ <svg class="mic-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
363
+ <path d="M12 14a3 3 0 003-3V5a3 3 0 00-6 0v6a3 3 0 003 3zm5-3a5 5 0 01-10 0H5a7 7 0 0014 0h-2z"/>
364
+ </svg>
365
+ </button>
366
+ <button id="math-stop_button" aria-label="Stop Speech" disabled>
367
+ <!-- Stop Icon SVG -->
368
+ <svg class="stop-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
369
+ <rect x="6" y="6" width="12" height="12" rx="2" ry="2"></rect>
370
+ </svg>
371
+ </button>
372
+ </div>
373
+ </div>
374
+
375
+ <!-- Loading Spinner -->
376
+ <div id="math-loading-spinner" class="hidden">
377
+ <div class="spinner"></div>
378
+ </div>
379
+
380
+ <!-- Configuration Info -->
381
+ <div id="math-configuration" class="hidden">
382
+ <p>Model Initialized: <span id="math-selected-model">N/A</span></p>
383
+ </div>
384
+ </div>
385
+ </div>
386
+ </div>
387
+ <!-- --------------------Math Content Ends----------------------------------------------- -->
388
+
389
+
390
+
391
+
392
+ <!-- --------------------Interdisciplinary Content Starts-------------------------------- -->
393
+ <div id="interdisciplinary" class="content-section hidden">
394
+ <div class="card interdisciplinary-card">
395
+ <!-- Interdisciplinary Studies Assistant Layout -->
396
+ <div class="inter-options">
397
+ <!-- Model Selection Section -->
398
+ <div id="inter-model-selection-container" class="option-section">
399
+ <label for="inter-model-selection">Select Model:</label>
400
+ <select id="inter-model-selection"></select>
401
+ <button id="inter-download" disabled>Download Model</button>
402
+ </div>
403
+
404
+ <!-- Voice Selection Section -->
405
+ <div id="inter-tool-selection-container" class="option-section">
406
+ <label for="inter-tools">Select Voice:</label>
407
+ <select id="inter-tools"></select>
408
+ </div>
409
+
410
+ <!-- Speech Controls Section -->
411
+ <div id="inter-speech-controls" class="option-section">
412
+ <label for="speech-rate">Speech Rate:</label>
413
+ <input type="range" id="speech-rate" min="0.5" max="2" step="0.1" value="1" disabled>
414
+
415
+ <label for="speech-pitch">Speech Pitch:</label>
416
+ <input type="range" id="speech-pitch" min="0" max="2" step="0.1" value="1" disabled>
417
+ </div>
418
+
419
+ <!-- Logs Section -->
420
+ <div id="inter-logs-container" class="option-section">
421
+ <h3>Logs</h3>
422
+ <div id="inter-logs">
423
+ <!-- Logs will appear here -->
424
+ </div>
425
+ <button id="inter-clear-logs">Clear Logs</button>
426
+ </div>
427
+ </div>
428
+
429
+ <div class="chat-window">
430
+ <!-- Chat Box Section -->
431
+ <div id="inter-chat-container">
432
+ <div id="inter-chat-box">
433
+ <!-- Chat messages will appear here -->
434
+ </div>
435
+
436
+ <!-- Chat Stats -->
437
+ <div id="inter-chat-stats" class="hidden">Runtime Stats: N/A</div>
438
+
439
+ <!-- Chat Input Section -->
440
+ <div id="inter-text-input-container">
441
+ <input type="text" id="inter-text-input" placeholder="Type your message here..." disabled>
442
+ <button id="inter-submit-button" disabled>Send</button>
443
+ </div>
444
+
445
+ <!-- Microphone Controls -->
446
+ <div id="inter-mic-container">
447
+ <button id="inter-start_button" aria-label="Start Voice Input" disabled>
448
+ <!-- Mic Icon SVG -->
449
+ <svg class="mic-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
450
+ <path d="M12 14a3 3 0 003-3V5a3 3 0 00-6 0v6a3 3 0 003 3zm5-3a5 5 0 01-10 0H5a7 7 0 0014 0h-2z"/>
451
+ </svg>
452
+ </button>
453
+ <button id="inter-stop_button" aria-label="Stop Speech" disabled>
454
+ <!-- Stop Icon SVG -->
455
+ <svg class="stop-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
456
+ <rect x="6" y="6" width="12" height="12" rx="2" ry="2"></rect>
457
+ </svg>
458
+ </button>
459
+ </div>
460
+ </div>
461
+
462
+ <!-- Loading Spinner -->
463
+ <div id="loading-spinner" class="hidden">
464
+ <div class="spinner"></div>
465
+ </div>
466
+
467
+ <!-- Configuration Info -->
468
+ <div id="inter-configuration" class="hidden">
469
+ <p>Model Initialized: <span id="inter-selected-model">N/A</span></p>
470
+ </div>
471
+ </div>
472
+ </div>
473
+ </div>
474
+ <!-- --------------------Interdisciplinary Content Ends------------------------------------- -->
475
+
476
+
477
+
478
+
479
+ <!-- --------------------Insights Content Starts------------------------------------------- -->
480
+ <div id="insights" class="content-section hidden">
481
+ <div class="card">
482
+ <h2>Insights</h2>
483
+ <p>Content for Document Insights will go here. Document Insights (PDF, Image), Translator, Youtube Summarizer, </p>
484
+ </div>
485
+ </div>
486
+ <!-- --------------------Insights Content Ends------------------------------------------- -->
487
+
488
+
489
+ <!-- --------------------Podcast Content Starts------------------------------------------- -->
490
+ <div id="podcast" class="content-section hidden">
491
+ <div class="card">
492
+ <h2>Podcast</h2>
493
+ <p>Content for Podcast will go here. Document Podcast, Conversational Podcast, </p>
494
+ </div>
495
+ </div>
496
+ <!-- --------------------Podcast Content Ends--------------------------------------------- -->
497
+
498
+
499
+
500
+ <!-- --------------------3d-explorer Content Starts------------------------------------------- -->
501
+ <div id="3d-explorer" class="content-section hidden">
502
+ <div class="card">
503
+ <h2>3D Explorer</h2>
504
+ <p>Content for 3D Explorer will go here.</p>
505
+ </div>
506
+ </div>
507
+ <!-- --------------------3d-explorer Content Ends--------------------------------------------- -->
508
+
509
+
510
+ <div id="knowledge-base" class="content-section hidden">
511
+ <div class="card">
512
+ <h2>Knowledge Base</h2>
513
+ <p>Content for Knowledge Base will go here, Treasured Teachings, Vintage Papers, Digital Dustbin.</p>
514
+ </div>
515
+ </div>
516
+
517
+ <div id="digital-twin" class="content-section hidden">
518
+ <div class="card">
519
+ <h2>Digital Twin</h2>
520
+ <p>Persona, Memory, Social Graph, Class Notes, Homework, Tracker.</p>
521
+ </div>
522
+ </div>
523
+
524
+ </div>
525
+ </div>
526
+
527
+
528
+ <!-- Include the necessary scripts -->
529
+ <script type="module">
530
+ // Sidebar navigation
531
+ const sidebarLinks = document.querySelectorAll('.sidebar a');
532
+ const contentSections = document.querySelectorAll('.content-section');
533
+
534
+ sidebarLinks.forEach(link => {
535
+ link.addEventListener('click', (e) => {
536
+ e.preventDefault();
537
+ // Remove active class from all links
538
+ sidebarLinks.forEach(l => l.classList.remove('active'));
539
+ // Add active class to the clicked link
540
+ link.classList.add('active');
541
+
542
+ // Hide all content sections
543
+ contentSections.forEach(section => section.classList.add('hidden'));
544
+
545
+ // Show the selected content section
546
+ const target = link.getAttribute('data-content');
547
+ document.getElementById(target).classList.remove('hidden');
548
+ });
549
+ });
550
+ </script>
551
+
552
+
553
+ <!-- Include the necessary scripts -->
554
+
555
+ <script type="module" src="digital.human.audio.js"> </script>
556
+ <script type="module" src="digital.human.video.js"> </script>
557
+ <script type="module" src="coder.js"> </script>
558
+ <script type="module" src="advanced.math.js"></script>
559
+ <script type="module" src="interdisciplinary.js"></script> <!-- New JS for Interdisciplinary -->
560
+
561
+ </body>
562
+ </html>
index.html ADDED
@@ -0,0 +1,562 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>Aged Guru</title>
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <!-- Google Fonts for better typography -->
8
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&family=Roboto:wght@400;500&display=swap" rel="stylesheet">
9
+ <!-- Include necessary CSS styles -->
10
+ <link rel="stylesheet" href="base.css">
11
+ <link rel="stylesheet" href="coder.css">
12
+ <link rel="stylesheet" href="digital.human.video.css">
13
+ <link rel="stylesheet" href="digital.human.audio.css">
14
+ <link rel="stylesheet" href="advanced.math.css">
15
+ <link rel="stylesheet" href="interdisciplinary.css"> <!-- New CSS for Interdisciplinary -->
16
+ </head>
17
+ <body>
18
+
19
+ <!-- Sidebar -->
20
+ <div class="sidebar">
21
+ <h2>AGED GURU</h2>
22
+ <a href="#" class="active" data-content="voice">Digital Human Voice</a>
23
+ <a href="#" data-content="video">Digital Human Video</a>
24
+ <a href="#" data-content="coder">Young Coder</a>
25
+ <a href="#" data-content="advanced-math">Advanced Mathematics & Problem Solving</a>
26
+ <a href="#" data-content="interdisciplinary">Interdisciplinary Studies Assistant</a>
27
+ <a href="#" data-content="insights">Insights</a>
28
+ <a href="#" data-content="podcast">Podcast</a>
29
+ <a href="#" data-content="3d-explorer">3D Explorer</a>
30
+ <a href="#" data-content="knowledge-base">Knowledge Base</a>
31
+ <a href="#" data-content="digital-twin">Digital Twin</a>
32
+ </div>
33
+
34
+ <!-- Main Content -->
35
+ <div class="main-content" id="main-content">
36
+ <div class="header">
37
+ <h1>5-MIN SCIENTIST PORTAL</h1>
38
+ <!-- You can add user profile or settings here -->
39
+ </div>
40
+ <!-- Content will be loaded here -->
41
+ <div id="content-area">
42
+
43
+
44
+ <!-- -----------------Digital Human Voice Content Starts---------------- -->
45
+ <div id="voice" class="content-section">
46
+ <div class="card voice-card">
47
+ <!-- Digital Human Voice Layout -->
48
+ <div class="voice-options">
49
+ <!-- Model Selection Section -->
50
+ <div id="voice-model-selection-container" class="option-section">
51
+ <label for="voice-model-selection">Select Model:</label>
52
+ <select id="voice-model-selection"></select>
53
+ <button id="voice-download" disabled>Download Model</button>
54
+ </div>
55
+
56
+ <!-- Voice Selection Section -->
57
+ <div id="voice-tool-selection-container" class="option-section">
58
+ <label for="voice-tools">Select Voice:</label>
59
+ <select id="voice-tools"></select>
60
+ </div>
61
+
62
+ <!-- Speech Controls Section -->
63
+ <div id="voice-speech-controls" class="option-section">
64
+ <label for="voice-speech-rate">Speech Rate:</label>
65
+ <input type="range" id="voice-speech-rate" min="0.5" max="2" step="0.1" value="1" disabled>
66
+
67
+ <label for="voice-speech-pitch">Speech Pitch:</label>
68
+ <input type="range" id="voice-speech-pitch" min="0" max="2" step="0.1" value="1" disabled>
69
+ </div>
70
+
71
+ <!-- Logs Section -->
72
+ <div id="voice-logs-container" class="option-section">
73
+ <h3>Logs</h3>
74
+ <div id="voice-logs">
75
+ <!-- Logs will appear here -->
76
+ </div>
77
+ <button id="voice-clear-logs">Clear Logs</button>
78
+ </div>
79
+ </div>
80
+
81
+ <div class="chat-window">
82
+ <!-- Chat Box Section -->
83
+ <div id="voice-chat-container">
84
+ <div id="voice-chat-box">
85
+ <!-- Chat messages will appear here -->
86
+ </div>
87
+
88
+ <!-- Chat Stats -->
89
+ <div id="voice-chat-stats" class="hidden">Runtime Stats: N/A</div>
90
+
91
+ <!-- Chat Input Section -->
92
+ <div id="voice-text-input-container">
93
+ <input type="text" id="voice-text-input" placeholder="Type your message here..." disabled>
94
+ <button id="voice-submit-button" disabled>Send</button>
95
+ </div>
96
+
97
+ <!-- Microphone Controls -->
98
+ <div id="voice-mic-container">
99
+ <button id="voice-start_button" aria-label="Start Voice Input" disabled>
100
+ <!-- Mic Icon SVG -->
101
+ <svg class="mic-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
102
+ <path d="M12 14a3 3 0 003-3V5a3 3 0 00-6 0v6a3 3 0 003 3zm5-3a5 5 0 01-10 0H5a7 7 0 0014 0h-2z"/>
103
+ </svg>
104
+ </button>
105
+ <button id="voice-stop_button" aria-label="Stop Speech" disabled>
106
+ <!-- Stop Icon SVG -->
107
+ <svg class="stop-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
108
+ <rect x="6" y="6" width="12" height="12" rx="2" ry="2"></rect>
109
+ </svg>
110
+ </button>
111
+ </div>
112
+ </div>
113
+
114
+ <!-- Loading Spinner -->
115
+ <div id="voice-loading-spinner" class="hidden">
116
+ <div class="spinner"></div>
117
+ </div>
118
+
119
+ <!-- Configuration Info -->
120
+ <div id="voice-configuration" class="hidden">
121
+ <p>Model Initialized: <span id="voice-selected-model">N/A</span></p>
122
+ </div>
123
+ </div>
124
+ </div>
125
+ </div>
126
+ <!-- -----------------Digital Human Voice Content Ends---------------- -->
127
+
128
+
129
+
130
+
131
+ <!-- --------------------Digital Human Mentor Video Content Starts---------------- -->
132
+ <div id="video" class="content-section hidden">
133
+ <div class="card video-card">
134
+ <!-- Digital Human Video Assistant Layout -->
135
+ <div class="video-options">
136
+ <!-- Model Selection Section -->
137
+ <div id="video-model-selection-container" class="option-section">
138
+ <label for="video-model-selection">Select Model:</label>
139
+ <select id="video-model-selection"></select>
140
+ <button id="video-download" disabled>Download Model</button>
141
+ </div>
142
+
143
+ <!-- Voice Selection Section -->
144
+ <div id="video-tool-selection-container" class="option-section">
145
+ <label for="video-tools">Select Voice:</label>
146
+ <select id="video-tools"></select>
147
+ </div>
148
+
149
+ <!-- Speech Controls Section -->
150
+ <div id="video-speech-controls" class="option-section">
151
+ <label for="video-speech-rate">Speech Rate:</label>
152
+ <input type="range" id="video-speech-rate" min="0.5" max="2" step="0.1" value="1" disabled>
153
+
154
+ <label for="video-speech-pitch">Speech Pitch:</label>
155
+ <input type="range" id="video-speech-pitch" min="0" max="2" step="0.1" value="1" disabled>
156
+ </div>
157
+
158
+ <!-- Logs Section -->
159
+ <div id="video-logs-container" class="option-section">
160
+ <h3>Logs</h3>
161
+ <div id="video-logs">
162
+ <!-- Logs will appear here -->
163
+ </div>
164
+ <button id="video-clear-logs">Clear Logs</button>
165
+ </div>
166
+ </div>
167
+
168
+ <div class="chat-window">
169
+ <!-- Chat Box Section -->
170
+ <div id="video-chat-container">
171
+ <div id="video-chat-box">
172
+ <!-- Chat messages will appear here -->
173
+ </div>
174
+
175
+ <!-- Chat Stats -->
176
+ <div id="video-chat-stats" class="hidden">Runtime Stats: N/A</div>
177
+
178
+ <!-- Chat Input Section -->
179
+ <div id="video-text-input-container">
180
+ <input type="text" id="video-text-input" placeholder="Type your message here..." disabled>
181
+ <button id="video-submit-button" disabled>Send</button>
182
+ </div>
183
+
184
+ <!-- Microphone Controls -->
185
+ <div id="video-mic-container">
186
+ <button id="video-start_button" aria-label="Start Voice Input" disabled>
187
+ <!-- Mic Icon SVG -->
188
+ <svg class="mic-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
189
+ <path d="M12 14a3 3 0 003-3V5a3 3 0 00-6 0v6a3 3 0 003 3zm5-3a5 5 0 01-10 0H5a7 7 0 0014 0h-2z"/>
190
+ </svg>
191
+ </button>
192
+ <button id="video-stop_button" aria-label="Stop Speech" disabled>
193
+ <!-- Stop Icon SVG -->
194
+ <svg class="stop-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
195
+ <rect x="6" y="6" width="12" height="12" rx="2" ry="2"></rect>
196
+ </svg>
197
+ </button>
198
+ </div>
199
+ </div>
200
+
201
+ <!-- Loading Spinner -->
202
+ <div id="video-loading-spinner" class="hidden">
203
+ <div class="spinner"></div>
204
+ </div>
205
+
206
+ <!-- Configuration Info -->
207
+ <div id="video-configuration" class="hidden">
208
+ <p>Model Initialized: <span id="video-selected-model">N/A</span></p>
209
+ </div>
210
+ </div>
211
+ </div>
212
+ </div>
213
+ <!-- --------------------Digital Human Mentor Video Content Ends----------------- -->
214
+
215
+
216
+
217
+
218
+
219
+ <!-- --------------------Coder Content Starts------------------------------------- -->
220
+ <div id="coder" class="content-section hidden">
221
+ <div class="card coder-card">
222
+ <!-- Young Coder Layout -->
223
+ <div class="coder-options">
224
+ <!-- Model Selection Section -->
225
+ <div id="coder-model-selection-container" class="option-section">
226
+ <label for="coder-model-selection">Select Model:</label>
227
+ <select id="coder-model-selection"></select>
228
+ <button id="coder-download" disabled>Download Model</button>
229
+ </div>
230
+
231
+ <!-- Tool Selection Section -->
232
+ <div id="coder-tool-selection-container" class="option-section">
233
+ <label for="coder-tools">Select Voice:</label>
234
+ <select id="coder-tools"></select>
235
+ </div>
236
+
237
+ <!-- Speech Controls Section -->
238
+ <div id="coder-speech-controls" class="option-section">
239
+ <label for="coder-speech-rate">Speech Rate:</label>
240
+ <input type="range" id="coder-speech-rate" min="0.5" max="2" step="0.1" value="1" disabled>
241
+
242
+ <label for="coder-speech-pitch">Speech Pitch:</label>
243
+ <input type="range" id="coder-speech-pitch" min="0" max="2" step="0.1" value="1" disabled>
244
+ </div>
245
+
246
+ <!-- Logs Section -->
247
+ <div id="coder-logs-container" class="option-section">
248
+ <h3>Logs</h3>
249
+ <div id="coder-logs">
250
+ <!-- Logs will appear here -->
251
+ </div>
252
+ <button id="coder-clear-logs">Clear Logs</button>
253
+ </div>
254
+ </div>
255
+
256
+ <div class="chat-window">
257
+ <!-- Chat Box Section -->
258
+ <div id="coder-chat-container">
259
+ <div id="coder-chat-box">
260
+ <!-- Chat messages will appear here -->
261
+ </div>
262
+
263
+ <!-- Chat Stats -->
264
+ <div id="coder-chat-stats" class="hidden">Runtime Stats: N/A</div>
265
+
266
+ <!-- Chat Input Section -->
267
+ <div id="coder-text-input-container">
268
+ <input type="text" id="coder-text-input" placeholder="Type your message here..." disabled>
269
+ <button id="coder-submit-button" disabled>Send</button>
270
+ </div>
271
+
272
+ <!-- Microphone Controls -->
273
+ <div id="coder-mic-container">
274
+ <button id="coder-start_button" aria-label="Start Voice Input" disabled>
275
+ <!-- Mic Icon SVG -->
276
+ <svg class="mic-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
277
+ <path d="M12 14a3 3 0 003-3V5a3 3 0 00-6 0v6a3 3 0 003 3zm5-3a5 5 0 01-10 0H5a7 7 0 0014 0h-2z"/>
278
+ </svg>
279
+ </button>
280
+ <button id="coder-stop_button" aria-label="Stop Speech" disabled>
281
+ <!-- Stop Icon SVG -->
282
+ <svg class="stop-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
283
+ <rect x="6" y="6" width="12" height="12" rx="2" ry="2"></rect>
284
+ </svg>
285
+ </button>
286
+ </div>
287
+ </div>
288
+
289
+ <!-- Loading Spinner -->
290
+ <div id="coder-loading-spinner" class="hidden">
291
+ <div class="spinner"></div>
292
+ </div>
293
+
294
+ <!-- Configuration Info -->
295
+ <div id="coder-configuration" class="hidden">
296
+ <p>Model Initialized: <span id="coder-selected-model">N/A</span></p>
297
+ </div>
298
+ </div>
299
+ </div>
300
+ </div>
301
+ <!-- --------------------Coder Content Ends---------------------------------------- -->
302
+
303
+
304
+
305
+ <!-- --------------------Math Content Starts--------------------------------------- -->
306
+ <div id="advanced-math" class="content-section hidden">
307
+ <div class="card advanced-math-card">
308
+ <!-- Advanced Mathematics & Problem Solving Layout -->
309
+ <div class="math-options">
310
+ <!-- Model Selection Section -->
311
+ <div id="math-model-selection-container" class="option-section">
312
+ <label for="math-model-selection">Select Math Model:</label>
313
+ <select id="math-model-selection"></select>
314
+ <button id="math-download" disabled>Download Model</button>
315
+ </div>
316
+
317
+ <!-- Tool Selection Section -->
318
+ <div id="math-tool-selection-container" class="option-section">
319
+ <label for="math-tools">Select Voice:</label>
320
+ <select id="math-tools"></select>
321
+ </div>
322
+
323
+ <!-- Speech Controls Section -->
324
+ <div id="math-speech-controls" class="option-section">
325
+ <label for="math-speech-rate">Speech Rate:</label>
326
+ <input type="range" id="math-speech-rate" min="0.5" max="2" step="0.1" value="1" disabled>
327
+
328
+ <label for="math-speech-pitch">Speech Pitch:</label>
329
+ <input type="range" id="math-speech-pitch" min="0" max="2" step="0.1" value="1" disabled>
330
+ </div>
331
+
332
+ <!-- Logs Section -->
333
+ <div id="math-logs-container" class="option-section">
334
+ <h3>Logs</h3>
335
+ <div id="math-logs">
336
+ <!-- Logs will appear here -->
337
+ </div>
338
+ <button id="math-clear-logs">Clear Logs</button>
339
+ </div>
340
+ </div>
341
+
342
+ <div class="chat-window">
343
+ <!-- Chat Box Section -->
344
+ <div id="math-chat-container">
345
+ <div id="math-chat-box">
346
+ <!-- Chat messages will appear here -->
347
+ </div>
348
+
349
+ <!-- Chat Stats -->
350
+ <div id="math-chat-stats" class="hidden">Runtime Stats: N/A</div>
351
+
352
+ <!-- Chat Input Section -->
353
+ <div id="math-text-input-container">
354
+ <input type="text" id="math-text-input" placeholder="Type your math problem here..." disabled>
355
+ <button id="math-submit-button" disabled>Submit</button>
356
+ </div>
357
+
358
+ <!-- Microphone Controls -->
359
+ <div id="math-mic-container">
360
+ <button id="math-start_button" aria-label="Start Voice Input" disabled>
361
+ <!-- Mic Icon SVG -->
362
+ <svg class="mic-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
363
+ <path d="M12 14a3 3 0 003-3V5a3 3 0 00-6 0v6a3 3 0 003 3zm5-3a5 5 0 01-10 0H5a7 7 0 0014 0h-2z"/>
364
+ </svg>
365
+ </button>
366
+ <button id="math-stop_button" aria-label="Stop Speech" disabled>
367
+ <!-- Stop Icon SVG -->
368
+ <svg class="stop-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
369
+ <rect x="6" y="6" width="12" height="12" rx="2" ry="2"></rect>
370
+ </svg>
371
+ </button>
372
+ </div>
373
+ </div>
374
+
375
+ <!-- Loading Spinner -->
376
+ <div id="math-loading-spinner" class="hidden">
377
+ <div class="spinner"></div>
378
+ </div>
379
+
380
+ <!-- Configuration Info -->
381
+ <div id="math-configuration" class="hidden">
382
+ <p>Model Initialized: <span id="math-selected-model">N/A</span></p>
383
+ </div>
384
+ </div>
385
+ </div>
386
+ </div>
387
+ <!-- --------------------Math Content Ends----------------------------------------------- -->
388
+
389
+
390
+
391
+
392
+ <!-- --------------------Interdisciplinary Content Starts-------------------------------- -->
393
+ <div id="interdisciplinary" class="content-section hidden">
394
+ <div class="card interdisciplinary-card">
395
+ <!-- Interdisciplinary Studies Assistant Layout -->
396
+ <div class="inter-options">
397
+ <!-- Model Selection Section -->
398
+ <div id="inter-model-selection-container" class="option-section">
399
+ <label for="inter-model-selection">Select Model:</label>
400
+ <select id="inter-model-selection"></select>
401
+ <button id="inter-download" disabled>Download Model</button>
402
+ </div>
403
+
404
+ <!-- Voice Selection Section -->
405
+ <div id="inter-tool-selection-container" class="option-section">
406
+ <label for="inter-tools">Select Voice:</label>
407
+ <select id="inter-tools"></select>
408
+ </div>
409
+
410
+ <!-- Speech Controls Section -->
411
+ <div id="inter-speech-controls" class="option-section">
412
+ <label for="speech-rate">Speech Rate:</label>
413
+ <input type="range" id="speech-rate" min="0.5" max="2" step="0.1" value="1" disabled>
414
+
415
+ <label for="speech-pitch">Speech Pitch:</label>
416
+ <input type="range" id="speech-pitch" min="0" max="2" step="0.1" value="1" disabled>
417
+ </div>
418
+
419
+ <!-- Logs Section -->
420
+ <div id="inter-logs-container" class="option-section">
421
+ <h3>Logs</h3>
422
+ <div id="inter-logs">
423
+ <!-- Logs will appear here -->
424
+ </div>
425
+ <button id="inter-clear-logs">Clear Logs</button>
426
+ </div>
427
+ </div>
428
+
429
+ <div class="chat-window">
430
+ <!-- Chat Box Section -->
431
+ <div id="inter-chat-container">
432
+ <div id="inter-chat-box">
433
+ <!-- Chat messages will appear here -->
434
+ </div>
435
+
436
+ <!-- Chat Stats -->
437
+ <div id="inter-chat-stats" class="hidden">Runtime Stats: N/A</div>
438
+
439
+ <!-- Chat Input Section -->
440
+ <div id="inter-text-input-container">
441
+ <input type="text" id="inter-text-input" placeholder="Type your message here..." disabled>
442
+ <button id="inter-submit-button" disabled>Send</button>
443
+ </div>
444
+
445
+ <!-- Microphone Controls -->
446
+ <div id="inter-mic-container">
447
+ <button id="inter-start_button" aria-label="Start Voice Input" disabled>
448
+ <!-- Mic Icon SVG -->
449
+ <svg class="mic-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
450
+ <path d="M12 14a3 3 0 003-3V5a3 3 0 00-6 0v6a3 3 0 003 3zm5-3a5 5 0 01-10 0H5a7 7 0 0014 0h-2z"/>
451
+ </svg>
452
+ </button>
453
+ <button id="inter-stop_button" aria-label="Stop Speech" disabled>
454
+ <!-- Stop Icon SVG -->
455
+ <svg class="stop-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
456
+ <rect x="6" y="6" width="12" height="12" rx="2" ry="2"></rect>
457
+ </svg>
458
+ </button>
459
+ </div>
460
+ </div>
461
+
462
+ <!-- Loading Spinner -->
463
+ <div id="loading-spinner" class="hidden">
464
+ <div class="spinner"></div>
465
+ </div>
466
+
467
+ <!-- Configuration Info -->
468
+ <div id="inter-configuration" class="hidden">
469
+ <p>Model Initialized: <span id="inter-selected-model">N/A</span></p>
470
+ </div>
471
+ </div>
472
+ </div>
473
+ </div>
474
+ <!-- --------------------Interdisciplinary Content Ends------------------------------------- -->
475
+
476
+
477
+
478
+
479
+ <!-- --------------------Insights Content Starts------------------------------------------- -->
480
+ <div id="insights" class="content-section hidden">
481
+ <div class="card">
482
+ <h2>Insights</h2>
483
+ <p>Content for Document Insights will go here. Document Insights (PDF, Image), Translator, Youtube Summarizer, </p>
484
+ </div>
485
+ </div>
486
+ <!-- --------------------Insights Content Ends------------------------------------------- -->
487
+
488
+
489
+ <!-- --------------------Podcast Content Starts------------------------------------------- -->
490
+ <div id="podcast" class="content-section hidden">
491
+ <div class="card">
492
+ <h2>Podcast</h2>
493
+ <p>Content for Podcast will go here. Document Podcast, Conversational Podcast, </p>
494
+ </div>
495
+ </div>
496
+ <!-- --------------------Podcast Content Ends--------------------------------------------- -->
497
+
498
+
499
+
500
+ <!-- --------------------3d-explorer Content Starts------------------------------------------- -->
501
+ <div id="3d-explorer" class="content-section hidden">
502
+ <div class="card">
503
+ <h2>3D Explorer</h2>
504
+ <p>Content for 3D Explorer will go here.</p>
505
+ </div>
506
+ </div>
507
+ <!-- --------------------3d-explorer Content Ends--------------------------------------------- -->
508
+
509
+
510
+ <div id="knowledge-base" class="content-section hidden">
511
+ <div class="card">
512
+ <h2>Knowledge Base</h2>
513
+ <p>Content for Knowledge Base will go here, Treasured Teachings, Vintage Papers, Digital Dustbin.</p>
514
+ </div>
515
+ </div>
516
+
517
+ <div id="digital-twin" class="content-section hidden">
518
+ <div class="card">
519
+ <h2>Digital Twin</h2>
520
+ <p>Persona, Memory, Social Graph, Class Notes, Homework, Tracker.</p>
521
+ </div>
522
+ </div>
523
+
524
+ </div>
525
+ </div>
526
+
527
+
528
+ <!-- Include the necessary scripts -->
529
+ <script type="module">
530
+ // Sidebar navigation
531
+ const sidebarLinks = document.querySelectorAll('.sidebar a');
532
+ const contentSections = document.querySelectorAll('.content-section');
533
+
534
+ sidebarLinks.forEach(link => {
535
+ link.addEventListener('click', (e) => {
536
+ e.preventDefault();
537
+ // Remove active class from all links
538
+ sidebarLinks.forEach(l => l.classList.remove('active'));
539
+ // Add active class to the clicked link
540
+ link.classList.add('active');
541
+
542
+ // Hide all content sections
543
+ contentSections.forEach(section => section.classList.add('hidden'));
544
+
545
+ // Show the selected content section
546
+ const target = link.getAttribute('data-content');
547
+ document.getElementById(target).classList.remove('hidden');
548
+ });
549
+ });
550
+ </script>
551
+
552
+
553
+ <!-- Include the necessary scripts -->
554
+
555
+ <script type="module" src="digital.human.audio.js"> </script>
556
+ <script type="module" src="digital.human.video.js"> </script>
557
+ <script type="module" src="coder.js"> </script>
558
+ <script type="module" src="advanced.math.js"></script>
559
+ <script type="module" src="interdisciplinary.js"></script> <!-- New JS for Interdisciplinary -->
560
+
561
+ </body>
562
+ </html>
interdisciplinary.css ADDED
@@ -0,0 +1,422 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* interdisciplinary.css */
2
+
3
+ /* Reset and Base Styles */
4
+ #interdisciplinary * {
5
+ box-sizing: border-box;
6
+ font-family: Arial, sans-serif;
7
+ }
8
+
9
+ /* Card Styling */
10
+ .interdisciplinary-card {
11
+ display: flex;
12
+ gap: 20px;
13
+ background: linear-gradient(135deg, #ffffff, #f1f8e9);
14
+ border-radius: 16px;
15
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05);
16
+ padding: 25px;
17
+ margin: 25px auto;
18
+
19
+ transition: transform 0.3s, box-shadow 0.3s, background 0.3s;
20
+ }
21
+
22
+ .interdisciplinary-card:hover {
23
+ transform: translateY(-5px);
24
+ box-shadow: 0 6px 25px rgba(0, 0, 0, 0.1);
25
+ background: linear-gradient(135deg, #e0f7fa, #ffffff);
26
+ }
27
+
28
+ /* Flex Layout for Inter Options and Chat Window */
29
+ .inter-options {
30
+ flex: 1;
31
+ display: flex;
32
+ flex-direction: column;
33
+ gap: 20px;
34
+ }
35
+
36
+ /* Ensure Inter Options takes up available vertical space */
37
+ .inter-options {
38
+ height: 100%;
39
+ }
40
+
41
+ /* Chat Window Styling */
42
+ .chat-window {
43
+ flex: 2;
44
+ display: flex;
45
+ flex-direction: column;
46
+ gap: 20px;
47
+ }
48
+
49
+ /* Option Sections Styling */
50
+ .option-section {
51
+ background-color: #ffffff;
52
+ padding: 15px;
53
+ border-radius: 12px;
54
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
55
+ transition: background 0.3s, box-shadow 0.3s;
56
+ }
57
+
58
+ .option-section:hover {
59
+ background-color: #f9f9f9;
60
+ }
61
+
62
+ /* Button Styling (Reused from Original) */
63
+ #interdisciplinary button {
64
+ background: linear-gradient(135deg, #00796B, #004D40);
65
+ border: none;
66
+ color: white;
67
+ padding: 12px 20px;
68
+ font-size: 1em;
69
+ border-radius: 8px;
70
+ cursor: pointer;
71
+ transition: background 0.3s, transform 0.2s, box-shadow 0.3s;
72
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
73
+ }
74
+
75
+ #interdisciplinary button:hover:not(:disabled) {
76
+ background: linear-gradient(135deg, #005D56, #00332E);
77
+ transform: translateY(-2px);
78
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
79
+ }
80
+
81
+ #interdisciplinary button:disabled {
82
+ background: linear-gradient(135deg, #A5D6A7, #81C784);
83
+ cursor: not-allowed;
84
+ box-shadow: none;
85
+ }
86
+
87
+ /* Select Dropdown Styling (Reused from Original) */
88
+ #interdisciplinary select {
89
+ width: 100%;
90
+ padding: 10px 15px;
91
+ border: 1px solid #cccccc;
92
+ border-radius: 8px;
93
+ font-size: 1em;
94
+ background-color: #ffffff;
95
+ transition: border-color 0.3s, box-shadow 0.3s;
96
+ margin-bottom: 15px;
97
+ }
98
+
99
+ #interdisciplinary select:focus {
100
+ border-color: #00796B;
101
+ box-shadow: 0 0 5px rgba(0, 121, 107, 0.5);
102
+ outline: none;
103
+ }
104
+
105
+ /* Speech Controls Styling */
106
+ #inter-speech-controls label {
107
+ display: block;
108
+ margin-bottom: 5px;
109
+ color: #555555;
110
+ font-weight: 500;
111
+ }
112
+
113
+ #inter-speech-controls input[type="range"] {
114
+ width: 100%;
115
+ margin-bottom: 15px;
116
+ }
117
+
118
+ /* Logs Container Styling */
119
+ #inter-logs-container {
120
+ display: flex;
121
+ flex-direction: column;
122
+ gap: 10px;
123
+ max-height: 200px; /* Set a maximum height for the logs container */
124
+ overflow: hidden; /* Hide any overflow to prevent the container from expanding */
125
+ }
126
+
127
+ #inter-logs {
128
+ flex: 1;
129
+ max-height: 200px; /* Adjust as needed */
130
+ border: 1px solid #e0e0e0;
131
+ border-radius: 12px;
132
+ overflow-y: auto; /* Enable vertical scrolling */
133
+ padding: 10px;
134
+ background-color: #f9f9f9;
135
+ font-size: 0.85em;
136
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.05);
137
+ }
138
+
139
+ #inter-clear-logs {
140
+ align-self: flex-end;
141
+ background: linear-gradient(135deg, #FF5252, #E53935);
142
+ border: none;
143
+ color: white;
144
+ padding: 8px 16px;
145
+ font-size: 0.9em;
146
+ cursor: pointer;
147
+ border-radius: 8px;
148
+ transition: background 0.3s, transform 0.2s, box-shadow 0.3s;
149
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
150
+ }
151
+
152
+ #inter-clear-logs:hover {
153
+ background: linear-gradient(135deg, #E53935, #D32F2F);
154
+ transform: scale(1.02);
155
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
156
+ }
157
+
158
+ /* Chat Box Styling */
159
+ #inter-chat-box {
160
+ height: 500px;
161
+ border: 1px solid #e0e0e0;
162
+ border-radius: 12px;
163
+ overflow-y: auto;
164
+ padding: 20px;
165
+ background-color: #ffffff;
166
+ position: relative;
167
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.05);
168
+ margin-bottom: 20px;
169
+ }
170
+
171
+ /* Message Styling */
172
+ #interdisciplinary .message-container {
173
+ margin-bottom: 20px;
174
+ display: flex;
175
+ }
176
+
177
+ #interdisciplinary .message {
178
+ padding: 12px 18px;
179
+ border-radius: 20px;
180
+ max-width: 75%;
181
+ position: relative;
182
+ word-wrap: break-word;
183
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
184
+ font-size: 0.95em;
185
+ line-height: 1.4;
186
+ background-color: #f1f8e9;
187
+ transition: background-color 0.3s, box-shadow 0.3s;
188
+ }
189
+
190
+ #interdisciplinary .user .message {
191
+ background: #DCF8C6;
192
+ border-bottom-right-radius: 0;
193
+ }
194
+
195
+ #interdisciplinary .assistant .message {
196
+ background: #e3f2fd;
197
+ border-bottom-left-radius: 0;
198
+ }
199
+
200
+ /* Chat Input Container Styling */
201
+ #inter-text-input-container {
202
+ display: flex;
203
+ gap: 10px;
204
+ }
205
+
206
+ #inter-text-input {
207
+ flex: 1;
208
+ padding: 10px 15px;
209
+ border: 1px solid #cccccc;
210
+ border-radius: 8px;
211
+ font-size: 1em;
212
+ background-color: #ffffff;
213
+ transition: border-color 0.3s, box-shadow 0.3s;
214
+ }
215
+
216
+ #inter-text-input:focus {
217
+ border-color: #00796B;
218
+ box-shadow: 0 0 5px rgba(0, 121, 107, 0.5);
219
+ outline: none;
220
+ }
221
+
222
+ #inter-submit-button {
223
+ background: linear-gradient(135deg, #00796B, #004D40);
224
+ border: none;
225
+ color: white;
226
+ padding: 10px 20px;
227
+ font-size: 1em;
228
+ border-radius: 8px;
229
+ cursor: pointer;
230
+ transition: background 0.3s, transform 0.2s, box-shadow 0.3s;
231
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
232
+ }
233
+
234
+ #inter-submit-button:hover:not(:disabled) {
235
+ background: linear-gradient(135deg, #005D56, #00332E);
236
+ transform: translateY(-2px);
237
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
238
+ }
239
+
240
+ #inter-submit-button:disabled {
241
+ background: linear-gradient(135deg, #A5D6A7, #81C784);
242
+ cursor: not-allowed;
243
+ box-shadow: none;
244
+ }
245
+
246
+ /* Microphone and Stop Buttons Styling */
247
+ #inter-mic-container {
248
+ display: flex;
249
+ justify-content: center;
250
+ gap: 15px;
251
+ margin-top: 20px;
252
+ }
253
+
254
+ #inter-start_button,
255
+ #inter-stop_button {
256
+ background: linear-gradient(135deg, #00796B, #004D40);
257
+ border: none;
258
+ color: white;
259
+ padding: 15px;
260
+ border-radius: 50%;
261
+ cursor: pointer;
262
+ transition: background 0.3s, transform 0.2s, box-shadow 0.3s;
263
+ width: 60px;
264
+ height: 60px;
265
+ display: flex;
266
+ justify-content: center;
267
+ align-items: center;
268
+ position: relative;
269
+ overflow: hidden;
270
+ box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
271
+ }
272
+
273
+ #inter-stop_button {
274
+ background: linear-gradient(135deg, #E53935, #D32F2F);
275
+ }
276
+
277
+ #inter-start_button::before,
278
+ #inter-stop_button::before {
279
+ content: '';
280
+ position: absolute;
281
+ width: 200%;
282
+ height: 200%;
283
+ background: rgba(255, 255, 255, 0.3);
284
+ top: 50%;
285
+ left: 50%;
286
+ transform: translate(-50%, -50%) scale(0);
287
+ border-radius: 50%;
288
+ transition: transform 0.5s ease-out;
289
+ }
290
+
291
+ #inter-start_button:active::before,
292
+ #inter-stop_button:active::before {
293
+ transform: translate(-50%, -50%) scale(1);
294
+ }
295
+
296
+ #inter-start_button:disabled,
297
+ #inter-stop_button:disabled {
298
+ background: linear-gradient(135deg, #A5D6A7, #81C784);
299
+ cursor: not-allowed;
300
+ box-shadow: none;
301
+ }
302
+
303
+ #inter-start_button:hover:not(:disabled),
304
+ #inter-stop_button:hover:not(:disabled) {
305
+ transform: scale(1.05);
306
+ box-shadow: 0 6px 15px rgba(0, 0, 0, 0.15);
307
+ }
308
+
309
+ #inter-stop_button:hover:not(:disabled) {
310
+ background: linear-gradient(135deg, #D32F2F, #C62828);
311
+ }
312
+
313
+ /* Icon Styling */
314
+ .interdisciplinary .mic-icon,
315
+ .interdisciplinary .stop-icon {
316
+ width: 30px;
317
+ height: 30px;
318
+ transition: transform 0.3s;
319
+ fill: #ffffff;
320
+ }
321
+
322
+ /* Animation for Mic Icon */
323
+ .interdisciplinary .mic-animate {
324
+ animation: pulse 2s infinite;
325
+ }
326
+
327
+ @keyframes pulse {
328
+ 0% {
329
+ transform: scale(1);
330
+ }
331
+ 50% {
332
+ transform: scale(1.2);
333
+ }
334
+ 100% {
335
+ transform: scale(1);
336
+ }
337
+ }
338
+
339
+ /* Chat Stats */
340
+ #inter-chat-stats {
341
+ font-size: 0.85em;
342
+ color: #555555;
343
+ text-align: center;
344
+ margin-top: 10px;
345
+ }
346
+
347
+ /* Hidden Class */
348
+ #interdisciplinary .hidden {
349
+ display: none;
350
+ }
351
+
352
+ /* Scrollbar Styling */
353
+ #inter-chat-box::-webkit-scrollbar,
354
+ #inter-logs::-webkit-scrollbar {
355
+ width: 8px;
356
+ }
357
+
358
+ #inter-chat-box::-webkit-scrollbar-track,
359
+ #inter-logs::-webkit-scrollbar-track {
360
+ background: #f1f1f1;
361
+ border-radius: 4px;
362
+ }
363
+
364
+ #inter-chat-box::-webkit-scrollbar-thumb,
365
+ #inter-logs::-webkit-scrollbar-thumb {
366
+ background: #cccccc;
367
+ border-radius: 4px;
368
+ }
369
+
370
+ #inter-chat-box::-webkit-scrollbar-thumb:hover,
371
+ #inter-logs::-webkit-scrollbar-thumb:hover {
372
+ background: #a8a8a8;
373
+ }
374
+
375
+ /* Initialize Button Styling (Reused from Original) */
376
+ #inter-download {
377
+ flex: 0 0 auto;
378
+ background: linear-gradient(135deg, #00796B, #004D40);
379
+ border: none;
380
+ color: white;
381
+ padding: 10px 20px;
382
+ font-size: 1em;
383
+ border-radius: 8px;
384
+ cursor: pointer;
385
+ transition: background 0.3s, transform 0.2s, box-shadow 0.3s;
386
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
387
+ }
388
+
389
+ #inter-download:hover:not(:disabled) {
390
+ background: linear-gradient(135deg, #005D56, #00332E);
391
+ transform: translateY(-2px);
392
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
393
+ }
394
+
395
+ #inter-download:disabled {
396
+ background: linear-gradient(135deg, #A5D6A7, #81C784);
397
+ cursor: not-allowed;
398
+ box-shadow: none;
399
+ }
400
+
401
+ /* Configuration Info Styling */
402
+ #inter-configuration {
403
+ margin-top: 15px;
404
+ font-size: 0.9em;
405
+ color: #555555;
406
+ }
407
+
408
+ /* Responsive Grid Adjustments */
409
+ @media (max-width: 800px) {
410
+ .interdisciplinary-card {
411
+ flex-direction: column;
412
+ }
413
+
414
+ /* Adjust Logs Container Height for Smaller Screens */
415
+ #inter-logs-container {
416
+ max-height: 200px;
417
+ }
418
+
419
+ #inter-logs {
420
+ max-height: 150px;
421
+ }
422
+ }
interdisciplinary.js ADDED
@@ -0,0 +1,612 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // interdisciplinary.js
2
+ import * as webllm from "https://esm.run/@mlc-ai/web-llm";
3
+
4
+ // Ensure the script runs after the DOM is fully loaded
5
+ document.addEventListener("DOMContentLoaded", () => {
6
+ // Initialize the Interdisciplinary Studies Assistant section
7
+ const interMessages = [
8
+ {
9
+ content: "You are Aged Guru, an intelligent assistant skilled in interdisciplinary studies. Provide insightful and comprehensive answers to complex interdisciplinary questions.",
10
+ role: "system"
11
+ }
12
+ ];
13
+
14
+ const interAvailableModels = webllm.prebuiltAppConfig.model_list.map(
15
+ (m) => m.model_id
16
+ );
17
+ let interSelectedModel = "Llama-3.2-3B-Instruct-q4f16_1-MLC"; // Default model
18
+
19
+ function interUpdateEngineInitProgressCallback(report) {
20
+ console.log("Interdisciplinary Initialize", report.progress);
21
+ // Instead of updating a status span, log the progress
22
+ logMessage(`Model Initialization Progress: ${report.text}`, "system");
23
+ }
24
+
25
+ const interEngine = new webllm.MLCEngine();
26
+ interEngine.setInitProgressCallback(interUpdateEngineInitProgressCallback);
27
+
28
+ let interIsGenerating = false; // Flag to prevent multiple generations
29
+
30
+ async function interInitializeWebLLMEngine() {
31
+ logMessage("Model initialization started.", "system");
32
+ document.getElementById("loading-spinner").classList.remove("hidden"); // Show spinner
33
+ interSelectedModel = document.getElementById("inter-model-selection").value;
34
+ const config = {
35
+ temperature: 0.7, // Adjusted for more precise answers
36
+ top_p: 0.9
37
+ };
38
+ try {
39
+ await interEngine.reload(interSelectedModel, config);
40
+ document.getElementById("inter-selected-model").textContent = interSelectedModel;
41
+ document.getElementById("inter-start_button").disabled = false;
42
+ document.getElementById("inter-text-input").disabled = false; // Enable text input after initialization
43
+ document.getElementById("inter-submit-button").disabled = false; // Enable submit button after initialization
44
+ document.getElementById("inter-speech-controls").disabled = false; // Enable speech controls after initialization
45
+ document.getElementById("inter-configuration").classList.remove("hidden");
46
+ logMessage("Model initialized successfully.", "system");
47
+ } catch (error) {
48
+ console.error("Error initializing the model:", error);
49
+ alert("Failed to initialize the model. Please try again.");
50
+ logMessage("Failed to initialize the model.", "error");
51
+ } finally {
52
+ document.getElementById("loading-spinner").classList.add("hidden"); // Hide spinner
53
+ }
54
+ }
55
+
56
+ async function interStreamingGenerating(messages, onUpdate, onFinish, onError) {
57
+ if (interIsGenerating) {
58
+ console.warn("Interdisciplinary Generation already in progress.");
59
+ return;
60
+ }
61
+ interIsGenerating = true;
62
+ try {
63
+ let curMessage = "";
64
+ const completion = await interEngine.chat.completions.create({
65
+ stream: true,
66
+ messages
67
+ });
68
+ for await (const chunk of completion) {
69
+ const curDelta = chunk.choices[0].delta.content;
70
+ if (curDelta) {
71
+ curMessage += curDelta;
72
+ }
73
+ onUpdate(curMessage);
74
+ }
75
+ const finalMessage = await interEngine.getMessage();
76
+ console.log(`Interdisciplinary Generated final message: ${finalMessage}`); // Debugging
77
+ onFinish(finalMessage);
78
+ logMessage("Response generated successfully.", "system");
79
+ } catch (err) {
80
+ console.error(err);
81
+ onError(err);
82
+ logMessage("An error occurred during response generation.", "error");
83
+ } finally {
84
+ interIsGenerating = false;
85
+ }
86
+ }
87
+
88
+ // Flag to track the last input method
89
+ let interLastInputWasVoice = false;
90
+
91
+ function interAppendMessage(message) {
92
+ console.log(`Interdisciplinary Appending message: ${message.content} (Role: ${message.role})`); // Debugging
93
+ const interChatBox = document.getElementById("inter-chat-box");
94
+
95
+ // Check if the assistant's message is already appended to avoid duplication
96
+ if (message.role === "assistant") {
97
+ const existingMessages = interChatBox.querySelectorAll(".message");
98
+ const lastMessage = existingMessages[existingMessages.length - 1];
99
+ if (lastMessage && lastMessage.textContent === message.content) {
100
+ console.warn("Duplicate assistant message detected in Interdisciplinary section, skipping append.");
101
+
102
+ // Only trigger TTS for assistant messages if the last input was via voice
103
+ if (message.role === "assistant" && message.content !== "typing..." && interLastInputWasVoice) {
104
+ interSpeak(message.content);
105
+ }
106
+
107
+ return; // Exit to avoid appending the same message twice
108
+ }
109
+ }
110
+
111
+ const container = document.createElement("div");
112
+ container.classList.add("message-container");
113
+ const newMessage = document.createElement("div");
114
+ newMessage.classList.add("message");
115
+ newMessage.textContent = message.content;
116
+
117
+ if (message.role === "user") {
118
+ container.classList.add("user");
119
+ } else {
120
+ container.classList.add("assistant");
121
+ }
122
+
123
+ container.appendChild(newMessage);
124
+ interChatBox.appendChild(container);
125
+ interChatBox.scrollTop = interChatBox.scrollHeight;
126
+
127
+ // Only trigger TTS for assistant messages if the last input was via voice
128
+ if (message.role === "assistant" && message.content !== "typing..." && interLastInputWasVoice) {
129
+ interSpeak(message.content);
130
+ }
131
+ }
132
+
133
+ function interUpdateLastMessage(content) {
134
+ const messageDoms = document.getElementById("inter-chat-box").querySelectorAll(".message");
135
+ const lastMessageDom = messageDoms[messageDoms.length - 1];
136
+ lastMessageDom.textContent = content;
137
+ }
138
+
139
+ function interOnSpeechRecognized(transcript) {
140
+ const input = transcript.trim();
141
+ const message = {
142
+ content: input,
143
+ role: "user"
144
+ };
145
+ if (input.length === 0) {
146
+ return;
147
+ }
148
+ interLastInputWasVoice = true; // Set flag as voice input
149
+ console.log(`Interdisciplinary Voice input received: ${input}`); // Debugging
150
+ document.getElementById("inter-start_button").disabled = true;
151
+ document.getElementById("inter-submit-button").disabled = true; // Disable submit button during processing
152
+
153
+ interMessages.push(message);
154
+ interAppendMessage(message);
155
+ logMessage(`User (Voice): ${input}`, "user");
156
+
157
+ // Append "typing..." placeholder
158
+ const aiPlaceholder = {
159
+ content: "typing...",
160
+ role: "assistant"
161
+ };
162
+ interAppendMessage(aiPlaceholder);
163
+ logMessage("InterBot is typing...", "system");
164
+
165
+ const onFinishGenerating = (finalMessage) => {
166
+ console.log(`Interdisciplinary Finishing generation with message: ${finalMessage}`); // Debugging
167
+ // Remove the "typing..." placeholder
168
+ const interChatBox = document.getElementById("inter-chat-box");
169
+ const lastMessageContainer = interChatBox.lastElementChild;
170
+ if (lastMessageContainer && lastMessageContainer.querySelector(".message").textContent === "typing...") {
171
+ interChatBox.removeChild(lastMessageContainer);
172
+ }
173
+
174
+ // Append the final message
175
+ const aiMessage = {
176
+ content: finalMessage,
177
+ role: "assistant"
178
+ };
179
+ interAppendMessage(aiMessage);
180
+ logMessage(`InterBot: ${finalMessage}`, "assistant");
181
+
182
+ document.getElementById("inter-start_button").disabled = false;
183
+ document.getElementById("inter-submit-button").disabled = false; // Re-enable submit button after processing
184
+ interEngine.runtimeStatsText().then((statsText) => {
185
+ document.getElementById("inter-chat-stats").classList.remove("hidden");
186
+ document.getElementById("inter-chat-stats").textContent = statsText;
187
+ logMessage(`Runtime Stats: ${statsText}`, "system");
188
+ });
189
+ };
190
+
191
+ interStreamingGenerating(
192
+ interMessages,
193
+ interUpdateLastMessage,
194
+ onFinishGenerating,
195
+ (err) => {
196
+ console.error(err);
197
+ alert("An error occurred while generating the response. Please try again.");
198
+ logMessage("Error during response generation.", "error");
199
+ document.getElementById("inter-start_button").disabled = false;
200
+ document.getElementById("inter-submit-button").disabled = false;
201
+ }
202
+ );
203
+ }
204
+
205
+ // Speech Recognition Code for Interdisciplinary
206
+ let interRecognizing = false;
207
+ let interIgnore_onend;
208
+ let interFinal_transcript = '';
209
+ let interRecognition;
210
+
211
+ function interStartButton(event) {
212
+ if (interRecognizing) {
213
+ interRecognition.stop();
214
+ return;
215
+ }
216
+ interFinal_transcript = '';
217
+ interRecognition.lang = 'en-US';
218
+ interRecognition.start();
219
+ interIgnore_onend = false;
220
+ document.getElementById("inter-start_button").classList.add("mic-animate");
221
+ logMessage("Voice input started.", "system");
222
+ }
223
+
224
+ if (!('webkitSpeechRecognition' in window)) {
225
+ alert("Web Speech API is not supported by this browser.");
226
+ logMessage("Web Speech API is not supported by this browser.", "error");
227
+ } else {
228
+ interRecognition = new webkitSpeechRecognition();
229
+ interRecognition.continuous = false; // Non-continuous recognition
230
+ interRecognition.interimResults = false; // Get only final results
231
+
232
+ interRecognition.onstart = function() {
233
+ interRecognizing = true;
234
+ logMessage("Speech recognition started.", "system");
235
+ };
236
+
237
+ interRecognition.onerror = function(event) {
238
+ if (event.error == 'no-speech') {
239
+ document.getElementById("inter-start_button").classList.remove("mic-animate");
240
+ alert('No speech was detected in Interdisciplinary section.');
241
+ logMessage("No speech detected.", "error");
242
+ interIgnore_onend = true;
243
+ }
244
+ if (event.error == 'audio-capture') {
245
+ document.getElementById("inter-start_button").classList.remove("mic-animate");
246
+ alert('No microphone was found in Interdisciplinary section.');
247
+ logMessage("No microphone found.", "error");
248
+ interIgnore_onend = true;
249
+ }
250
+ if (event.error == 'not-allowed') {
251
+ alert('Permission to use microphone was denied in Interdisciplinary section.');
252
+ logMessage("Microphone permission denied.", "error");
253
+ interIgnore_onend = true;
254
+ }
255
+ };
256
+
257
+ interRecognition.onend = function() {
258
+ interRecognizing = false;
259
+ document.getElementById("inter-start_button").classList.remove("mic-animate");
260
+ logMessage("Speech recognition ended.", "system");
261
+ if (interIgnore_onend) {
262
+ return;
263
+ }
264
+ if (!interFinal_transcript) {
265
+ logMessage("No transcript captured.", "error");
266
+ return;
267
+ }
268
+ // Process the final transcript
269
+ interOnSpeechRecognized(interFinal_transcript);
270
+ };
271
+
272
+ interRecognition.onresult = function(event) {
273
+ for (let i = event.resultIndex; i < event.results.length; ++i) {
274
+ if (event.results[i].isFinal) {
275
+ interFinal_transcript += event.results[i][0].transcript;
276
+ }
277
+ }
278
+ interFinal_transcript = interFinal_transcript.trim();
279
+ logMessage(`Recognized Speech: ${interFinal_transcript}`, "user");
280
+ };
281
+ }
282
+
283
+ document.getElementById("inter-start_button").addEventListener("click", function(event) {
284
+ interStartButton(event);
285
+ });
286
+
287
+ // Initialize Model Selection
288
+ interAvailableModels.forEach((modelId) => {
289
+ const option = document.createElement("option");
290
+ option.value = modelId;
291
+ option.textContent = modelId;
292
+ document.getElementById("inter-model-selection").appendChild(option);
293
+ });
294
+ document.getElementById("inter-model-selection").value = interSelectedModel;
295
+
296
+ // **Enable the Download Model button after models are loaded**
297
+ document.getElementById("inter-download").disabled = false;
298
+
299
+ document.getElementById("inter-download").addEventListener("click", function () {
300
+ interInitializeWebLLMEngine().then(() => {
301
+ document.getElementById("inter-start_button").disabled = false;
302
+ // Enable speech controls after model initialization
303
+ document.getElementById("speech-rate").disabled = false;
304
+ document.getElementById("speech-pitch").disabled = false;
305
+ logMessage("Model download initiated.", "system");
306
+ });
307
+ });
308
+
309
+ document.getElementById("inter-clear-logs").addEventListener("click", function () {
310
+ document.getElementById("inter-logs").innerHTML = '';
311
+ logMessage("Logs cleared.", "system");
312
+ });
313
+
314
+ // ===== TTS Integration =====
315
+
316
+ // Initialize Speech Synthesis
317
+ let interSpeech = new SpeechSynthesisUtterance();
318
+ interSpeech.lang = "en";
319
+
320
+ let interVoices = [];
321
+
322
+ // Use addEventListener instead of directly assigning to onvoiceschanged
323
+ window.speechSynthesis.addEventListener("voiceschanged", () => {
324
+ interVoices = window.speechSynthesis.getVoices();
325
+ interPopulateVoices();
326
+ });
327
+
328
+ function interPopulateVoices() {
329
+ const voiceSelect = document.getElementById("inter-tools");
330
+ voiceSelect.innerHTML = ''; // Clear existing options
331
+ interVoices.forEach((voice, i) => {
332
+ const option = new Option(voice.name, i);
333
+ voiceSelect.appendChild(option);
334
+ });
335
+ if (interVoices.length > 0) {
336
+ const savedVoice = localStorage.getItem("selectedVoice");
337
+ if (savedVoice !== null && interVoices[savedVoice]) {
338
+ interSpeech.voice = interVoices[savedVoice];
339
+ voiceSelect.value = savedVoice;
340
+ } else {
341
+ interSpeech.voice = interVoices[0];
342
+ }
343
+ }
344
+ }
345
+
346
+ // Voice Selection Event Listener
347
+ document.getElementById("inter-tools").addEventListener("change", () => {
348
+ const selectedVoiceIndex = document.getElementById("inter-tools").value;
349
+ interSpeech.voice = interVoices[selectedVoiceIndex];
350
+ // Save to localStorage
351
+ localStorage.setItem("selectedVoice", selectedVoiceIndex);
352
+ logMessage(`Voice changed to: ${interVoices[selectedVoiceIndex].name}`, "system");
353
+ });
354
+
355
+ // Function to Speak Text with Voice Selection and Handling Large Texts
356
+ function interSpeak(text) {
357
+ if (!window.speechSynthesis) {
358
+ console.warn("Speech Synthesis not supported in this browser for Interdisciplinary section.");
359
+ logMessage("Speech Synthesis not supported in this browser.", "error");
360
+ return;
361
+ }
362
+
363
+ // Show spinner and enable Stop button
364
+ document.getElementById("loading-spinner").classList.remove("hidden");
365
+ document.getElementById("inter-stop_button").disabled = false;
366
+ logMessage("TTS started.", "system");
367
+
368
+ // Retrieve the currently selected voice
369
+ const selectedVoice = interSpeech.voice;
370
+
371
+ // Split the text into sentences to manage large texts
372
+ const sentences = text.match(/[^\.!\?]+[\.!\?]+/g) || [text];
373
+
374
+ let utterancesCount = sentences.length;
375
+
376
+ sentences.forEach(sentence => {
377
+ const utterance = new SpeechSynthesisUtterance(sentence.trim());
378
+
379
+ // Assign the selected voice to the utterance
380
+ if (selectedVoice) {
381
+ utterance.voice = selectedVoice;
382
+ }
383
+
384
+ // Assign rate and pitch from sliders
385
+ const rate = parseFloat(document.getElementById("speech-rate").value);
386
+ const pitch = parseFloat(document.getElementById("speech-pitch").value);
387
+ utterance.rate = rate; // Adjust the speaking rate (0.1 to 10)
388
+ utterance.pitch = pitch; // Adjust the pitch (0 to 2)
389
+
390
+ // Add event listeners for debugging or additional functionality
391
+ utterance.onstart = () => {
392
+ console.log("Speech started:", sentence);
393
+ logMessage(`TTS started: ${sentence.trim()}`, "system");
394
+ };
395
+
396
+ utterance.onend = () => {
397
+ console.log("Speech ended:", sentence);
398
+ logMessage(`TTS ended: ${sentence.trim()}`, "system");
399
+ utterancesCount--;
400
+ if (utterancesCount === 0) {
401
+ // Hide spinner and disable Stop button when all utterances have been spoken
402
+ document.getElementById("loading-spinner").classList.add("hidden");
403
+ document.getElementById("inter-stop_button").disabled = true;
404
+ logMessage("All TTS messages have been spoken.", "system");
405
+ }
406
+ };
407
+
408
+ utterance.onerror = (e) => {
409
+ console.error("Speech Synthesis Error:", e);
410
+ alert("An error occurred during speech synthesis. Please try again.");
411
+ logMessage("Speech synthesis encountered an error.", "error");
412
+ utterancesCount = 0;
413
+ document.getElementById("loading-spinner").classList.add("hidden");
414
+ document.getElementById("inter-stop_button").disabled = true;
415
+ };
416
+
417
+ window.speechSynthesis.speak(utterance);
418
+ });
419
+ }
420
+
421
+ // ===== New: Stop Speech Functionality =====
422
+
423
+ /**
424
+ * Stops any ongoing speech synthesis.
425
+ */
426
+ function interStopSpeech() {
427
+ if (window.speechSynthesis.speaking) {
428
+ window.speechSynthesis.cancel();
429
+ document.getElementById("loading-spinner").classList.add("hidden");
430
+ document.getElementById("inter-stop_button").disabled = true;
431
+ logMessage("Speech synthesis stopped by user.", "system");
432
+ }
433
+ }
434
+
435
+ // Event Listener for Stop Button
436
+ document.getElementById("inter-stop_button").addEventListener("click", function () {
437
+ interStopSpeech();
438
+ });
439
+
440
+ // ===== New: Text Input Handling =====
441
+
442
+ // Function to Handle Text Submission
443
+ function interHandleTextSubmit() {
444
+ const textInput = document.getElementById("inter-text-input");
445
+ const input = textInput.value.trim();
446
+ if (input.length === 0) {
447
+ return;
448
+ }
449
+ textInput.value = ''; // Clear the input field
450
+ const message = {
451
+ content: input,
452
+ role: "user" // Ensure this is correctly set
453
+ };
454
+ console.log(`Interdisciplinary Text input received: ${input}`); // Debugging
455
+ logMessage(`User: ${input}`, "user");
456
+
457
+ interLastInputWasVoice = false; // Set flag as text input
458
+ document.getElementById("inter-submit-button").disabled = true; // Disable to prevent multiple submissions
459
+
460
+ interMessages.push(message);
461
+ interAppendMessage(message);
462
+
463
+ // Append "typing..." placeholder
464
+ const aiPlaceholder = {
465
+ content: "typing...",
466
+ role: "assistant"
467
+ };
468
+ interAppendMessage(aiPlaceholder);
469
+ logMessage("InterBot is typing...", "system");
470
+
471
+ const onFinishGenerating = (finalMessage) => {
472
+ console.log(`Interdisciplinary Finishing generation with message: ${finalMessage}`); // Debugging
473
+ // Remove the "typing..." placeholder
474
+ const interChatBox = document.getElementById("inter-chat-box");
475
+ const lastMessageContainer = interChatBox.lastElementChild;
476
+ if (lastMessageContainer && lastMessageContainer.querySelector(".message").textContent === "typing...") {
477
+ interChatBox.removeChild(lastMessageContainer);
478
+ }
479
+
480
+ // Append the final message
481
+ const aiMessage = {
482
+ content: finalMessage,
483
+ role: "assistant"
484
+ };
485
+ interAppendMessage(aiMessage);
486
+ logMessage(`InterBot: ${finalMessage}`, "assistant");
487
+
488
+ // Trigger TTS for assistant messages if required
489
+ if (interLastInputWasVoice) {
490
+ interSpeak(finalMessage);
491
+ }
492
+
493
+ document.getElementById("inter-submit-button").disabled = false; // Re-enable submit button after processing
494
+ interEngine.runtimeStatsText().then((statsText) => {
495
+ document.getElementById("inter-chat-stats").classList.remove("hidden");
496
+ document.getElementById("inter-chat-stats").textContent = statsText;
497
+ logMessage(`Runtime Stats: ${statsText}`, "system");
498
+ });
499
+ };
500
+
501
+ interStreamingGenerating(
502
+ interMessages,
503
+ interUpdateLastMessage,
504
+ onFinishGenerating,
505
+ (err) => {
506
+ console.error(err);
507
+ alert("An error occurred while generating the response. Please try again.");
508
+ logMessage("Error during response generation.", "error");
509
+ document.getElementById("inter-submit-button").disabled = false;
510
+ }
511
+ );
512
+ }
513
+
514
+ // Event Listener for Submit Button
515
+ document.getElementById("inter-submit-button").addEventListener("click", function () {
516
+ interHandleTextSubmit();
517
+ });
518
+
519
+ // Event Listener for Enter Key in Text Input
520
+ document.getElementById("inter-text-input").addEventListener("keypress", function (e) {
521
+ if (e.key === 'Enter') {
522
+ interHandleTextSubmit();
523
+ }
524
+ });
525
+
526
+ // ===== Persisting User Preferences =====
527
+
528
+ // Load Preferences on Initialization
529
+ window.addEventListener("load", () => {
530
+ const savedVoice = localStorage.getItem("selectedVoice");
531
+ if (savedVoice !== null && interVoices[savedVoice]) {
532
+ document.getElementById("inter-tools").value = savedVoice;
533
+ interSpeech.voice = interVoices[savedVoice];
534
+ logMessage(`Loaded saved voice: ${interVoices[savedVoice].name}`, "system");
535
+ }
536
+
537
+ const savedRate = localStorage.getItem("speechRate");
538
+ if (savedRate !== null) {
539
+ document.getElementById("speech-rate").value = savedRate;
540
+ interSpeech.rate = parseFloat(savedRate);
541
+ logMessage(`Loaded saved speech rate: ${savedRate}`, "system");
542
+ }
543
+
544
+ const savedPitch = localStorage.getItem("speechPitch");
545
+ if (savedPitch !== null) {
546
+ document.getElementById("speech-pitch").value = savedPitch;
547
+ interSpeech.pitch = parseFloat(savedPitch);
548
+ logMessage(`Loaded saved speech pitch: ${savedPitch}`, "system");
549
+ }
550
+ });
551
+
552
+ // Save Speech Rate
553
+ document.getElementById("speech-rate").addEventListener("input", (e) => {
554
+ const rate = e.target.value;
555
+ interSpeech.rate = parseFloat(rate);
556
+ localStorage.setItem("speechRate", rate);
557
+ logMessage(`Speech rate changed to: ${rate}`, "system");
558
+ });
559
+
560
+ // Save Speech Pitch
561
+ document.getElementById("speech-pitch").addEventListener("input", (e) => {
562
+ const pitch = e.target.value;
563
+ interSpeech.pitch = parseFloat(pitch);
564
+ localStorage.setItem("speechPitch", pitch);
565
+ logMessage(`Speech pitch changed to: ${pitch}`, "system");
566
+ });
567
+
568
+ // ===== Logging Function =====
569
+
570
+ /**
571
+ * Logs messages to the #inter-logs container.
572
+ * @param {string} message - The message to log.
573
+ * @param {string} type - The type of message: 'user', 'assistant', 'system', 'error'.
574
+ */
575
+ function logMessage(message, type) {
576
+ const interLogs = document.getElementById("inter-logs");
577
+ const logEntry = document.createElement("div");
578
+ logEntry.classList.add("log-entry");
579
+ logEntry.textContent = `[${type.toUpperCase()}] ${message}`;
580
+
581
+ // Style log entries based on type
582
+ switch(type) {
583
+ case 'user':
584
+ logEntry.style.color = "#00796B";
585
+ break;
586
+ case 'assistant':
587
+ logEntry.style.color = "#004D40";
588
+ break;
589
+ case 'system':
590
+ logEntry.style.color = "#555555";
591
+ break;
592
+ case 'error':
593
+ logEntry.style.color = "#E53935";
594
+ break;
595
+ default:
596
+ logEntry.style.color = "#000000";
597
+ }
598
+
599
+ interLogs.appendChild(logEntry);
600
+ interLogs.scrollTop = interLogs.scrollHeight;
601
+ }
602
+
603
+ // ===== TTS Integration Continued =====
604
+
605
+ // Optional: Global Listener to Detect When All Speech Has Finished
606
+ window.speechSynthesis.addEventListener('end', () => {
607
+ console.log("All interdisciplinary speech has been spoken.");
608
+ logMessage("All TTS messages have been spoken.", "system");
609
+ // Ensure Stop button is disabled after speech ends
610
+ document.getElementById("inter-stop_button").disabled = true;
611
+ });
612
+ });