digitalhuman / index.backup8.html
atlury's picture
Rename index.html to index.backup8.html
daf5836 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Aged Guru Education Assistant</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Google Fonts for better typography -->
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&family=Roboto:wght@400;500&display=swap" rel="stylesheet">
<!-- Include necessary CSS styles -->
<style>
/* Reset and base styles */
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Inter', sans-serif;
background-color: #f9f9f9;
display: flex;
min-height: 100vh;
color: #333;
overflow: hidden;
}
/* Sidebar styling */
.sidebar {
width: 260px;
background-color: #ffffff;
box-shadow: 2px 0 5px rgba(0,0,0,0.1);
flex-shrink: 0;
display: flex;
flex-direction: column;
padding: 30px 20px;
position: fixed;
height: 100%;
overflow-y: auto;
transition: width 0.3s ease;
}
.sidebar h2 {
text-align: center;
margin-bottom: 40px;
font-family: 'Roboto', sans-serif;
font-weight: 600;
font-size: 1.6em;
color: #4A4A4A;
}
.sidebar a {
color: #555555;
padding: 12px 15px;
text-decoration: none;
font-size: 1em;
border-radius: 8px;
margin-bottom: 10px;
transition: background-color 0.3s, color 0.3s, transform 0.2s;
display: flex;
align-items: center;
}
.sidebar a:hover {
background-color: #f0f0f0;
color: #333333;
transform: translateX(5px);
}
.sidebar a.active {
background-color: #e0f7fa;
color: #00796B;
font-weight: 500;
}
.sidebar a::before {
content: '';
display: inline-block;
width: 6px;
height: 6px;
background-color: #00796B;
border-radius: 50%;
margin-right: 10px;
opacity: 0;
transition: opacity 0.3s;
}
.sidebar a.active::before {
opacity: 1;
}
/* Main content styling */
.main-content {
margin-left: 260px;
padding: 30px;
flex: 1;
background-color: #f9f9f9;
overflow-y: auto;
height: 100vh;
transition: margin-left 0.3s ease;
}
/* Header */
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 25px;
}
.header h1 {
font-family: 'Roboto', sans-serif;
font-size: 2em;
color: #333333;
}
/* Card Styling */
.card {
background-color: #ffffff;
border-radius: 12px;
box-shadow: 0 4px 10px rgba(0,0,0,0.05);
padding: 25px;
margin-bottom: 25px;
transition: transform 0.3s, box-shadow 0.3s;
}
.card:hover {
transform: translateY(-5px);
box-shadow: 0 6px 15px rgba(0,0,0,0.1);
}
/* Button Styling */
button {
background-color: #00796B;
border: none;
color: white;
padding: 12px 20px;
font-size: 1em;
border-radius: 8px;
cursor: pointer;
transition: background-color 0.3s, transform 0.2s;
}
button:hover {
background-color: #005D56;
transform: translateY(-2px);
}
button:disabled {
background-color: #A5D6A7;
cursor: not-allowed;
}
/* Select Dropdown Styling */
select {
width: 100%;
padding: 10px 15px;
border: 1px solid #cccccc;
border-radius: 8px;
font-size: 1em;
background-color: #ffffff;
transition: border-color 0.3s;
margin-bottom: 15px;
}
select:focus {
border-color: #00796B;
outline: none;
}
/* Chat Box Styling */
#chat-box {
height: 400px;
border: 1px solid #e0e0e0;
border-radius: 12px;
overflow-y: auto;
padding: 20px;
background-color: #fafafa;
margin-bottom: 20px;
position: relative;
box-shadow: inset 0 2px 4px rgba(0,0,0,0.05);
}
.message-container {
margin-bottom: 20px;
display: flex;
}
.message {
padding: 12px 18px;
border-radius: 20px;
max-width: 75%;
position: relative;
word-wrap: break-word;
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
font-size: 0.95em;
line-height: 1.4;
}
.user {
justify-content: flex-end;
}
.user .message {
background-color: #DCF8C6;
border-bottom-right-radius: 0;
}
.assistant {
justify-content: flex-start;
}
.assistant .message {
background-color: #FFFFFF;
border-bottom-left-radius: 0;
}
/* Microphone Button Styling */
#mic-container {
text-align: center;
margin-bottom: 20px;
}
/* Updated Start Button */
#start_button {
background-color: #00796B;
border: none;
color: white;
padding: 15px;
border-radius: 50%;
cursor: pointer;
transition: background-color 0.3s, transform 0.2s;
width: 60px;
height: 60px;
display: flex;
justify-content: center;
align-items: center;
position: relative;
overflow: hidden;
}
#start_button::before {
content: '';
position: absolute;
width: 200%;
height: 200%;
background: rgba(255, 255, 255, 0.3);
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scale(0);
border-radius: 50%;
transition: transform 0.5s ease-out;
}
#start_button:active::before {
transform: translate(-50%, -50%) scale(1);
}
#start_button:disabled {
background-color: #A5D6A7;
cursor: not-allowed;
}
#start_button:hover:not(:disabled) {
background-color: #005D56;
transform: scale(1.05);
}
/* Icon Styling */
.mic-icon {
width: 30px;
height: 30px;
transition: transform 0.3s;
}
/* Animation for Mic Icon */
.mic-animate {
animation: pulse 2s infinite;
}
@keyframes pulse {
0% {
transform: scale(1);
}
50% {
transform: scale(1.2);
}
100% {
transform: scale(1);
}
}
/* Logs Styling */
#logs {
height: 120px;
border: 1px solid #e0e0e0;
border-radius: 12px;
overflow-y: auto;
padding: 15px;
background-color: #f1f1f1;
font-size: 0.85em;
margin-bottom: 10px;
box-shadow: inset 0 2px 4px rgba(0,0,0,0.05);
}
#clear-logs {
background-color: #FF5252;
border: none;
color: white;
padding: 10px 18px;
font-size: 0.9em;
cursor: pointer;
border-radius: 8px;
transition: background-color 0.3s, transform 0.2s;
}
#clear-logs:hover {
background-color: #E53935;
transform: scale(1.02);
}
/* Chat Stats */
#chat-stats {
font-size: 0.85em;
color: #888888;
text-align: center;
margin-top: 10px;
}
/* Voice Selection Container */
#tts-voice-container label {
display: block;
margin-bottom: 8px;
color: #555555;
font-weight: 500;
}
/* Hidden Class */
.hidden {
display: none;
}
/* Scrollbar Styling */
#chat-box::-webkit-scrollbar,
#logs::-webkit-scrollbar {
width: 8px;
}
#chat-box::-webkit-scrollbar-track,
#logs::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 4px;
}
#chat-box::-webkit-scrollbar-thumb,
#logs::-webkit-scrollbar-thumb {
background: #cccccc;
border-radius: 4px;
}
#chat-box::-webkit-scrollbar-thumb:hover,
#logs::-webkit-scrollbar-thumb:hover {
background: #a8a8a8;
}
/* Responsive Adjustments */
@media (max-width: 768px) {
.sidebar {
width: 220px;
}
.main-content {
margin-left: 220px;
}
}
@media (max-width: 600px) {
.sidebar {
position: relative;
width: 100%;
height: auto;
flex-direction: row;
overflow-x: auto;
padding: 15px 10px;
}
.sidebar h2 {
display: none;
}
.sidebar a {
margin-right: 10px;
margin-bottom: 0;
padding: 10px 12px;
font-size: 0.9em;
}
.main-content {
margin-left: 0;
padding: 20px 15px;
}
.header {
flex-direction: column;
align-items: flex-start;
}
.header h1 {
margin-bottom: 10px;
font-size: 1.5em;
}
}
/* Controls Container Styling */
#controls {
display: flex;
gap: 10px;
margin-bottom: 15px;
flex-wrap: wrap;
}
/* Initialize Button Styling */
#download {
flex: 0 0 auto;
background-color: #00796B;
border: none;
color: white;
padding: 10px 20px;
font-size: 1em;
border-radius: 8px;
cursor: pointer;
transition: background-color 0.3s, transform 0.2s;
}
#download:hover {
background-color: #005D56;
transform: translateY(-2px);
}
#download:disabled {
background-color: #A5D6A7;
cursor: not-allowed;
}
/* Enhanced Layout for Digital Human Mentor Voice Section */
#voice .card {
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 20px;
}
/* Adjust controls and voice selection */
#model-selection-container,
#tts-voice-container {
display: flex;
flex-direction: column;
}
#model-selection-container label,
#tts-voice-container label {
margin-bottom: 5px;
font-weight: 500;
color: #555555;
}
/* Chat and Controls Layout */
#chat-container {
display: flex;
flex-direction: column;
}
/* Ensure chat-box takes full width */
#chat-box {
width: 100%;
}
/* Adjust microphone container */
#mic-container {
display: flex;
justify-content: center;
margin-bottom: 20px;
}
/* Configuration Info */
#configuration {
margin-top: 15px;
font-size: 0.9em;
color: #555555;
}
/* Responsive Grid Adjustments */
@media (max-width: 800px) {
#voice .card {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<!-- Sidebar -->
<div class="sidebar">
<h2>Aged Guru</h2>
<a href="#" class="active" data-content="voice">Digital Human Mentor Voice</a>
<a href="#" data-content="video">Digital Human Mentor Video</a>
<a href="#" data-content="math-assistant">Virtual Math Assistant</a>
<a href="#" data-content="advanced-math">Advanced Mathematics & Problem Solving</a>
<a href="#" data-content="interdisciplinary">Interdisciplinary Studies Assistant</a>
<a href="#" data-content="document-insights">Document Insights</a>
<a href="#" data-content="3d-explorer">3D Explorer</a>
<a href="#" data-content="knowledge-base">Knowledge Base</a>
<a href="#" data-content="">Digital Twin</a>
<a href="#" data-content="">Student</a>
<a href="#" data-content="">Teacher</a>
</div>
<!-- Main Content -->
<div class="main-content" id="main-content">
<div class="header">
<h1>Aged Guru Education Assistant</h1>
<!-- You can add user profile or settings here -->
</div>
<!-- Content will be loaded here -->
<div id="content-area">
<!-- Digital Human Mentor Voice Content Starts -->
<div id="voice" class="content-section">
<div class="card">
<!-- Model Selection Section -->
<div id="model-selection-container">
<label for="model-selection">Select Language Model:</label>
<select id="model-selection"></select>
<button id="download">Initialize</button>
</div>
<!-- Voice Selection Section -->
<div id="tts-voice-container">
<label for="voices">Select Voice:</label>
<select id="voices"></select>
</div>
</div>
<div id="chat-container">
<div id="configuration" class="hidden">
<div id="model-info">
<strong>LLM:</strong> <span id="selected-model">Not Initialized</span>
</div>
</div>
<div id="chat-box"></div>
<!-- Microphone button -->
<div id="mic-container">
<button id="start_button" disabled>
<svg class="mic-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
<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"/>
</svg>
</button>
</div>
<div id="chat-stats" class="hidden"></div>
</div>
<h3>Logs</h3>
<div id="logs">
<div id="download-status" class="hidden"></div>
</div>
<button id="clear-logs">Clear Logs</button>
</div>
<!-- Digital Human Mentor Voice Content Ends -->
<!-- Placeholder for other sections -->
<div id="video" class="content-section hidden">
<div class="card">
<h2>Interactive Digital Human Mentor Video</h2>
<p>Content for Video Mentor will go here.</p>
</div>
</div>
<div id="math-assistant" class="content-section hidden">
<div class="card">
<h2>Virtual Math Assistant</h2>
<p>Content for Virtual Math Assistant will go here.</p>
</div>
</div>
<div id="advanced-math" class="content-section hidden">
<div class="card">
<h2>Advanced Mathematics & Problem Solving</h2>
<p>Content for Advanced Mathematics & Problem Solving will go here.</p>
</div>
</div>
<div id="interdisciplinary" class="content-section hidden">
<div class="card">
<h2>Interdisciplinary Studies Assistant</h2>
<p>Content for Interdisciplinary Studies Assistant will go here.</p>
</div>
</div>
<div id="document-insights" class="content-section hidden">
<div class="card">
<h2>Document Insights</h2>
<p>Content for Document Insights will go here.</p>
</div>
</div>
<div id="3d-explorer" class="content-section hidden">
<div class="card">
<h2>3D Explorer</h2>
<p>Content for 3D Explorer will go here.</p>
</div>
</div>
<div id="knowledge-base" class="content-section hidden">
<div class="card">
<h2>Knowledge Base</h2>
<p>Content for Knowledge Base will go here.</p>
</div>
</div>
</div>
</div>
<!-- Include the necessary scripts -->
<script type="module">
import * as webllm from "https://esm.run/@mlc-ai/web-llm";
// Sidebar navigation
const sidebarLinks = document.querySelectorAll('.sidebar a');
const contentSections = document.querySelectorAll('.content-section');
sidebarLinks.forEach(link => {
link.addEventListener('click', (e) => {
e.preventDefault();
// Remove active class from all links
sidebarLinks.forEach(l => l.classList.remove('active'));
// Add active class to the clicked link
link.classList.add('active');
// Hide all content sections
contentSections.forEach(section => section.classList.add('hidden'));
// Show the selected content section
const target = link.getAttribute('data-content');
document.getElementById(target).classList.remove('hidden');
});
});
// Initialize the Digital Human Mentor Voice section
const messages = [
{
content: "You are Chatbot, a friendly, helpful robot called Aged Guru. Respond to what the user said in a creative and helpful way, but keep your responses brief.\n" +
"Keep responses brief and legible. Your output will be converted to audio.\n" +
"Your responses will converted to audio. Please do not include any special characters in your response other than '!' or '?'.",
role: "system"
}
];
const availableModels = webllm.prebuiltAppConfig.model_list.map(
(m) => m.model_id
);
let selectedModel = "TinyLlama-1.1B-Chat-v0.4-q4f32_1-MLC-1k";
function updateEngineInitProgressCallback(report) {
console.log("initialize", report.progress);
document.getElementById("download-status").textContent = report.text;
}
const engine = new webllm.MLCEngine();
engine.setInitProgressCallback(updateEngineInitProgressCallback);
async function initializeWebLLMEngine() {
document.getElementById("download-status").classList.remove("hidden");
selectedModel = document.getElementById("model-selection").value;
const config = {
temperature: 1.0,
top_p: 1
};
await engine.reload(selectedModel, config);
document.getElementById("selected-model").textContent = selectedModel;
document.getElementById("start_button").disabled = false;
document.getElementById("configuration").classList.remove("hidden");
}
async function streamingGenerating(messages, onUpdate, onFinish, onError) {
try {
let curMessage = "";
const completion = await engine.chat.completions.create({
stream: true,
messages
});
for await (const chunk of completion) {
const curDelta = chunk.choices[0].delta.content;
if (curDelta) {
curMessage += curDelta;
}
onUpdate(curMessage);
}
const finalMessage = await engine.getMessage();
onFinish(finalMessage);
} catch (err) {
onError(err);
}
}
function appendMessage(message) {
const chatBox = document.getElementById("chat-box");
const container = document.createElement("div");
container.classList.add("message-container");
const newMessage = document.createElement("div");
newMessage.classList.add("message");
newMessage.textContent = message.content;
if (message.role === "user") {
container.classList.add("user");
} else {
container.classList.add("assistant");
}
container.appendChild(newMessage);
chatBox.appendChild(container);
chatBox.scrollTop = chatBox.scrollHeight;
// If the message is from the assistant and not a placeholder, trigger TTS
if (message.role === "assistant" && message.content !== "typing...") {
speak(message.content);
}
}
function updateLastMessage(content) {
const messageDoms = document.getElementById("chat-box").querySelectorAll(".message");
const lastMessageDom = messageDoms[messageDoms.length - 1];
lastMessageDom.textContent = content;
}
function onSpeechRecognized(transcript) {
const input = transcript.trim();
const message = {
content: input,
role: "user"
};
if (input.length === 0) {
return;
}
document.getElementById("start_button").disabled = true;
messages.push(message);
appendMessage(message);
// Append "typing..." placeholder
const aiPlaceholder = {
content: "typing...",
role: "assistant"
};
appendMessage(aiPlaceholder);
const onFinishGenerating = (finalMessage) => {
// Remove the "typing..." placeholder
const chatBox = document.getElementById("chat-box");
const lastMessageContainer = chatBox.lastElementChild;
if (lastMessageContainer && lastMessageContainer.querySelector(".message").textContent === "typing...") {
chatBox.removeChild(lastMessageContainer);
}
// Append the final message
const aiMessage = {
content: finalMessage,
role: "assistant"
};
appendMessage(aiMessage);
document.getElementById("start_button").disabled = false;
engine.runtimeStatsText().then((statsText) => {
document.getElementById("chat-stats").classList.remove("hidden");
document.getElementById("chat-stats").textContent = statsText;
});
};
streamingGenerating(
messages,
updateLastMessage,
onFinishGenerating,
console.error
);
}
// Speech recognition code
var recognizing = false;
var ignore_onend;
var final_transcript = '';
var recognition;
function startButton(event) {
if (recognizing) {
recognition.stop();
return;
}
final_transcript = '';
recognition.lang = 'en-US';
recognition.start();
ignore_onend = false;
document.getElementById("start_button").classList.add("mic-animate");
}
if (!('webkitSpeechRecognition' in window)) {
alert("Web Speech API is not supported by this browser.");
} else {
recognition = new webkitSpeechRecognition();
recognition.continuous = false; // Non-continuous recognition
recognition.interimResults = false; // Get only final results
recognition.onstart = function() {
recognizing = true;
};
recognition.onerror = function(event) {
if (event.error == 'no-speech') {
document.getElementById("start_button").classList.remove("mic-animate");
alert('No speech was detected.');
ignore_onend = true;
}
if (event.error == 'audio-capture') {
document.getElementById("start_button").classList.remove("mic-animate");
alert('No microphone was found.');
ignore_onend = true;
}
if (event.error == 'not-allowed') {
alert('Permission to use microphone was denied.');
ignore_onend = true;
}
};
recognition.onend = function() {
recognizing = false;
document.getElementById("start_button").classList.remove("mic-animate");
if (ignore_onend) {
return;
}
if (!final_transcript) {
return;
}
// Process the final transcript
onSpeechRecognized(final_transcript);
};
recognition.onresult = function(event) {
for (var i = event.resultIndex; i < event.results.length; ++i) {
if (event.results[i].isFinal) {
final_transcript += event.results[i][0].transcript;
}
}
final_transcript = final_transcript.trim();
};
}
document.getElementById("start_button").addEventListener("click", function(event) {
startButton(event);
});
// Initialize model selection
availableModels.forEach((modelId) => {
const option = document.createElement("option");
option.value = modelId;
option.textContent = modelId;
document.getElementById("model-selection").appendChild(option);
});
document.getElementById("model-selection").value = selectedModel;
document.getElementById("download").addEventListener("click", function () {
initializeWebLLMEngine().then(() => {
document.getElementById("start_button").disabled = false;
});
});
document.getElementById("clear-logs").addEventListener("click", function () {
document.getElementById("logs").innerHTML = '';
});
// ===== TTS Integration =====
// Initialize Speech Synthesis
let speech = new SpeechSynthesisUtterance();
speech.lang = "en";
let voices = [];
window.speechSynthesis.onvoiceschanged = () => {
voices = window.speechSynthesis.getVoices();
populateVoices();
};
function populateVoices() {
const voiceSelect = document.getElementById("voices");
voiceSelect.innerHTML = ''; // Clear existing options
voices.forEach((voice, i) => {
const option = new Option(voice.name, i);
voiceSelect.appendChild(option);
});
if (voices.length > 0) {
speech.voice = voices[0];
}
}
// Voice Selection Event Listener
document.getElementById("voices").addEventListener("change", () => {
const selectedVoiceIndex = document.getElementById("voices").value;
speech.voice = voices[selectedVoiceIndex];
});
// Function to speak text
function speak(text) {
if (window.speechSynthesis.speaking) {
window.speechSynthesis.cancel(); // Cancel previous speech
}
speech.text = text;
window.speechSynthesis.speak(speech);
}
</script>
</body>
</html>