Update templates/index.html
Browse files- templates/index.html +165 -165
templates/index.html
CHANGED
|
@@ -1,165 +1,165 @@
|
|
| 1 |
-
<!doctype html>
|
| 2 |
-
<html>
|
| 3 |
-
<head>
|
| 4 |
-
<meta charset="utf-8" />
|
| 5 |
-
<title>Ortho Buddy β Voice</title>
|
| 6 |
-
<style>
|
| 7 |
-
html,body { height:100%; margin:0; font-family: "Segoe UI", Roboto, Arial; background: radial-gradient(circle at 50% 10%, #144a2b, #062219); color:#bfe6c9; }
|
| 8 |
-
.container { width:360px; margin:40px auto; text-align:center; }
|
| 9 |
-
h1 { letter-spacing:4px; font-size:36px; color:#2de08b; text-shadow:0 6px 30px rgba(0,0,0,0.7); margin:20px 0; }
|
| 10 |
-
.subtitle { color:#d6f3de; margin-bottom:10px; font-size:24px; }
|
| 11 |
-
.robot { width:270px; height:270px; background: url('robot.gif') center/contain no-repeat; margin: 40px auto; border-radius:8px; }
|
| 12 |
-
.control { margin-top:20px; }
|
| 13 |
-
.record-btn { width:50px; height:50px; border-radius:60px; border:none; background:linear-gradient(rgb(248, 245, 248), rgb(248, 245, 248)); box-shadow: 0 10px 30px rgba(0,0,0,0.6); color:white; font-size:18px; cursor:pointer; }
|
| 14 |
-
.record-btn.recording { background: linear-gradient(#ff6666, #cc2222); box-shadow: 0 10px 30px rgba(0,0,0,0.7); }
|
| 15 |
-
.heading {
|
| 16 |
-
text-align: center;
|
| 17 |
-
}
|
| 18 |
-
.reset-btn {
|
| 19 |
-
position: fixed;
|
| 20 |
-
top: 20px;
|
| 21 |
-
right: 20px;
|
| 22 |
-
padding: 10px 20px;
|
| 23 |
-
background: linear-gradient(#2de08b, #0a8f5c);
|
| 24 |
-
border: none;
|
| 25 |
-
border-radius: 6px;
|
| 26 |
-
font-size: 16px;
|
| 27 |
-
font-weight: bold;
|
| 28 |
-
color: #062219;
|
| 29 |
-
cursor: pointer;
|
| 30 |
-
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
|
| 31 |
-
z-index: 1000;
|
| 32 |
-
transition: background 0.3s ease;
|
| 33 |
-
}
|
| 34 |
-
|
| 35 |
-
.reset-btn:hover {
|
| 36 |
-
background: linear-gradient(#1bc47a, #07734f);
|
| 37 |
-
}
|
| 38 |
-
|
| 39 |
-
</style>
|
| 40 |
-
<h1 class = "heading">Summit Dental & Orthopedics</h1>
|
| 41 |
-
</head>
|
| 42 |
-
<body>
|
| 43 |
-
<div class="container">
|
| 44 |
-
|
| 45 |
-
<div class="subtitle"><b>KAMMI</b></div>
|
| 46 |
-
|
| 47 |
-
<div class="robot" id="robotImg"></div>
|
| 48 |
-
|
| 49 |
-
<div class="control">
|
| 50 |
-
<button id="recBtn" class="record-btn">π€</button>
|
| 51 |
-
</div>
|
| 52 |
-
|
| 53 |
-
<button id="resetBtn" class="reset-btn">New User</button>
|
| 54 |
-
|
| 55 |
-
<div class="text-stream" id="textStream"></div>
|
| 56 |
-
|
| 57 |
-
<!-- Hidden audio player for streamed voice -->
|
| 58 |
-
<audio id="player" controls autoplay hidden></audio>
|
| 59 |
-
</div>
|
| 60 |
-
|
| 61 |
-
<script>
|
| 62 |
-
let mediaRecorder;
|
| 63 |
-
let audioChunks = [];
|
| 64 |
-
let recBtn = document.getElementById("recBtn");
|
| 65 |
-
let textStream = document.getElementById("textStream");
|
| 66 |
-
let recording = false;
|
| 67 |
-
let player = document.getElementById("player");
|
| 68 |
-
|
| 69 |
-
document.getElementById("resetBtn").addEventListener("click", async () => {
|
| 70 |
-
try {
|
| 71 |
-
const response = await fetch("
|
| 72 |
-
method: "POST"
|
| 73 |
-
});
|
| 74 |
-
|
| 75 |
-
if (response.ok) {
|
| 76 |
-
showTempMessage("Please proceed.", "lightgreen");
|
| 77 |
-
} else {
|
| 78 |
-
const errorText = await response.text();
|
| 79 |
-
showTempMessage("Reset failed: " + errorText, "#ffb3b3");
|
| 80 |
-
}
|
| 81 |
-
} catch (error) {
|
| 82 |
-
showTempMessage("Reset error: " + error.message, "#ffb3b3");
|
| 83 |
-
}
|
| 84 |
-
});
|
| 85 |
-
|
| 86 |
-
// Utility function to show a message for 2 seconds
|
| 87 |
-
function showTempMessage(msg, color) {
|
| 88 |
-
const msgDiv = document.createElement("div");
|
| 89 |
-
msgDiv.style.color = color;
|
| 90 |
-
msgDiv.textContent = msg;
|
| 91 |
-
textStream.appendChild(msgDiv);
|
| 92 |
-
|
| 93 |
-
setTimeout(() => {
|
| 94 |
-
msgDiv.remove();
|
| 95 |
-
}, 2000);
|
| 96 |
-
}
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
recBtn.addEventListener("click", async () => {
|
| 100 |
-
if (!recording) {
|
| 101 |
-
await startRecording();
|
| 102 |
-
} else {
|
| 103 |
-
stopRecordingAndSend();
|
| 104 |
-
}
|
| 105 |
-
recording = !recording;
|
| 106 |
-
recBtn.textContent = recording ? "Stop" : "π€";
|
| 107 |
-
recBtn.classList.toggle("recording", recording);
|
| 108 |
-
});
|
| 109 |
-
|
| 110 |
-
async function startRecording() {
|
| 111 |
-
textStream.innerHTML = ""; // clear previous
|
| 112 |
-
audioChunks = [];
|
| 113 |
-
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
|
| 114 |
-
alert("Your browser does not support microphone capture.");
|
| 115 |
-
return;
|
| 116 |
-
}
|
| 117 |
-
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
| 118 |
-
mediaRecorder = new MediaRecorder(stream);
|
| 119 |
-
mediaRecorder.ondataavailable = e => {
|
| 120 |
-
if (e.data && e.data.size > 0) audioChunks.push(e.data);
|
| 121 |
-
};
|
| 122 |
-
mediaRecorder.start();
|
| 123 |
-
}
|
| 124 |
-
|
| 125 |
-
function stopRecordingAndSend() {
|
| 126 |
-
if (!mediaRecorder) return;
|
| 127 |
-
mediaRecorder.stop();
|
| 128 |
-
mediaRecorder.onstop = async () => {
|
| 129 |
-
const audioBlob = new Blob(audioChunks, { type: 'audio/webm' });
|
| 130 |
-
const form = new FormData();
|
| 131 |
-
form.append("file", audioBlob, "recording.webm");
|
| 132 |
-
|
| 133 |
-
// fetch streaming audio directly
|
| 134 |
-
console.log("Sending audio to server...");
|
| 135 |
-
const resp = await fetch("
|
| 136 |
-
method: "POST",
|
| 137 |
-
body: form,
|
| 138 |
-
});
|
| 139 |
-
|
| 140 |
-
if (!resp.ok) {
|
| 141 |
-
const txt = await resp.text();
|
| 142 |
-
textStream.innerHTML += "<div style='color:#ffb3b3'>Server error: " + txt + "</div>";
|
| 143 |
-
return;
|
| 144 |
-
}
|
| 145 |
-
|
| 146 |
-
// create an object URL from streaming response
|
| 147 |
-
const mediaSource = new MediaSource();
|
| 148 |
-
player.src = URL.createObjectURL(mediaSource);
|
| 149 |
-
mediaSource.addEventListener('sourceopen', async () => {
|
| 150 |
-
const sourceBuffer = mediaSource.addSourceBuffer('audio/mpeg');
|
| 151 |
-
const reader = resp.body.getReader();
|
| 152 |
-
while (true) {
|
| 153 |
-
const { done, value } = await reader.read();
|
| 154 |
-
if (done) break;
|
| 155 |
-
sourceBuffer.appendBuffer(value);
|
| 156 |
-
await new Promise(resolve => sourceBuffer.addEventListener('updateend', resolve, { once: true }));
|
| 157 |
-
}
|
| 158 |
-
mediaSource.endOfStream();
|
| 159 |
-
});
|
| 160 |
-
player.play();
|
| 161 |
-
};
|
| 162 |
-
}
|
| 163 |
-
</script>
|
| 164 |
-
</body>
|
| 165 |
-
</html>
|
|
|
|
| 1 |
+
<!doctype html>
|
| 2 |
+
<html>
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="utf-8" />
|
| 5 |
+
<title>Ortho Buddy β Voice</title>
|
| 6 |
+
<style>
|
| 7 |
+
html,body { height:100%; margin:0; font-family: "Segoe UI", Roboto, Arial; background: radial-gradient(circle at 50% 10%, #144a2b, #062219); color:#bfe6c9; }
|
| 8 |
+
.container { width:360px; margin:40px auto; text-align:center; }
|
| 9 |
+
h1 { letter-spacing:4px; font-size:36px; color:#2de08b; text-shadow:0 6px 30px rgba(0,0,0,0.7); margin:20px 0; }
|
| 10 |
+
.subtitle { color:#d6f3de; margin-bottom:10px; font-size:24px; }
|
| 11 |
+
.robot { width:270px; height:270px; background: url('/static/robot.gif') center/contain no-repeat; margin: 40px auto; border-radius:8px; }
|
| 12 |
+
.control { margin-top:20px; }
|
| 13 |
+
.record-btn { width:50px; height:50px; border-radius:60px; border:none; background:linear-gradient(rgb(248, 245, 248), rgb(248, 245, 248)); box-shadow: 0 10px 30px rgba(0,0,0,0.6); color:white; font-size:18px; cursor:pointer; }
|
| 14 |
+
.record-btn.recording { background: linear-gradient(#ff6666, #cc2222); box-shadow: 0 10px 30px rgba(0,0,0,0.7); }
|
| 15 |
+
.heading {
|
| 16 |
+
text-align: center;
|
| 17 |
+
}
|
| 18 |
+
.reset-btn {
|
| 19 |
+
position: fixed;
|
| 20 |
+
top: 20px;
|
| 21 |
+
right: 20px;
|
| 22 |
+
padding: 10px 20px;
|
| 23 |
+
background: linear-gradient(#2de08b, #0a8f5c);
|
| 24 |
+
border: none;
|
| 25 |
+
border-radius: 6px;
|
| 26 |
+
font-size: 16px;
|
| 27 |
+
font-weight: bold;
|
| 28 |
+
color: #062219;
|
| 29 |
+
cursor: pointer;
|
| 30 |
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
|
| 31 |
+
z-index: 1000;
|
| 32 |
+
transition: background 0.3s ease;
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
.reset-btn:hover {
|
| 36 |
+
background: linear-gradient(#1bc47a, #07734f);
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
</style>
|
| 40 |
+
<h1 class = "heading">Summit Dental & Orthopedics</h1>
|
| 41 |
+
</head>
|
| 42 |
+
<body>
|
| 43 |
+
<div class="container">
|
| 44 |
+
|
| 45 |
+
<div class="subtitle"><b>KAMMI</b></div>
|
| 46 |
+
|
| 47 |
+
<div class="robot" id="robotImg"></div>
|
| 48 |
+
|
| 49 |
+
<div class="control">
|
| 50 |
+
<button id="recBtn" class="record-btn">π€</button>
|
| 51 |
+
</div>
|
| 52 |
+
|
| 53 |
+
<button id="resetBtn" class="reset-btn">New User</button>
|
| 54 |
+
|
| 55 |
+
<div class="text-stream" id="textStream"></div>
|
| 56 |
+
|
| 57 |
+
<!-- Hidden audio player for streamed voice -->
|
| 58 |
+
<audio id="player" controls autoplay hidden></audio>
|
| 59 |
+
</div>
|
| 60 |
+
|
| 61 |
+
<script>
|
| 62 |
+
let mediaRecorder;
|
| 63 |
+
let audioChunks = [];
|
| 64 |
+
let recBtn = document.getElementById("recBtn");
|
| 65 |
+
let textStream = document.getElementById("textStream");
|
| 66 |
+
let recording = false;
|
| 67 |
+
let player = document.getElementById("player");
|
| 68 |
+
|
| 69 |
+
document.getElementById("resetBtn").addEventListener("click", async () => {
|
| 70 |
+
try {
|
| 71 |
+
const response = await fetch("/reset_chat", {
|
| 72 |
+
method: "POST"
|
| 73 |
+
});
|
| 74 |
+
|
| 75 |
+
if (response.ok) {
|
| 76 |
+
showTempMessage("Please proceed.", "lightgreen");
|
| 77 |
+
} else {
|
| 78 |
+
const errorText = await response.text();
|
| 79 |
+
showTempMessage("Reset failed: " + errorText, "#ffb3b3");
|
| 80 |
+
}
|
| 81 |
+
} catch (error) {
|
| 82 |
+
showTempMessage("Reset error: " + error.message, "#ffb3b3");
|
| 83 |
+
}
|
| 84 |
+
});
|
| 85 |
+
|
| 86 |
+
// Utility function to show a message for 2 seconds
|
| 87 |
+
function showTempMessage(msg, color) {
|
| 88 |
+
const msgDiv = document.createElement("div");
|
| 89 |
+
msgDiv.style.color = color;
|
| 90 |
+
msgDiv.textContent = msg;
|
| 91 |
+
textStream.appendChild(msgDiv);
|
| 92 |
+
|
| 93 |
+
setTimeout(() => {
|
| 94 |
+
msgDiv.remove();
|
| 95 |
+
}, 2000);
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
recBtn.addEventListener("click", async () => {
|
| 100 |
+
if (!recording) {
|
| 101 |
+
await startRecording();
|
| 102 |
+
} else {
|
| 103 |
+
stopRecordingAndSend();
|
| 104 |
+
}
|
| 105 |
+
recording = !recording;
|
| 106 |
+
recBtn.textContent = recording ? "Stop" : "π€";
|
| 107 |
+
recBtn.classList.toggle("recording", recording);
|
| 108 |
+
});
|
| 109 |
+
|
| 110 |
+
async function startRecording() {
|
| 111 |
+
textStream.innerHTML = ""; // clear previous
|
| 112 |
+
audioChunks = [];
|
| 113 |
+
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
|
| 114 |
+
alert("Your browser does not support microphone capture.");
|
| 115 |
+
return;
|
| 116 |
+
}
|
| 117 |
+
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
| 118 |
+
mediaRecorder = new MediaRecorder(stream);
|
| 119 |
+
mediaRecorder.ondataavailable = e => {
|
| 120 |
+
if (e.data && e.data.size > 0) audioChunks.push(e.data);
|
| 121 |
+
};
|
| 122 |
+
mediaRecorder.start();
|
| 123 |
+
}
|
| 124 |
+
|
| 125 |
+
function stopRecordingAndSend() {
|
| 126 |
+
if (!mediaRecorder) return;
|
| 127 |
+
mediaRecorder.stop();
|
| 128 |
+
mediaRecorder.onstop = async () => {
|
| 129 |
+
const audioBlob = new Blob(audioChunks, { type: 'audio/webm' });
|
| 130 |
+
const form = new FormData();
|
| 131 |
+
form.append("file", audioBlob, "recording.webm");
|
| 132 |
+
|
| 133 |
+
// fetch streaming audio directly
|
| 134 |
+
console.log("Sending audio to server...");
|
| 135 |
+
const resp = await fetch("/chat_stream", {
|
| 136 |
+
method: "POST",
|
| 137 |
+
body: form,
|
| 138 |
+
});
|
| 139 |
+
|
| 140 |
+
if (!resp.ok) {
|
| 141 |
+
const txt = await resp.text();
|
| 142 |
+
textStream.innerHTML += "<div style='color:#ffb3b3'>Server error: " + txt + "</div>";
|
| 143 |
+
return;
|
| 144 |
+
}
|
| 145 |
+
|
| 146 |
+
// create an object URL from streaming response
|
| 147 |
+
const mediaSource = new MediaSource();
|
| 148 |
+
player.src = URL.createObjectURL(mediaSource);
|
| 149 |
+
mediaSource.addEventListener('sourceopen', async () => {
|
| 150 |
+
const sourceBuffer = mediaSource.addSourceBuffer('audio/mpeg');
|
| 151 |
+
const reader = resp.body.getReader();
|
| 152 |
+
while (true) {
|
| 153 |
+
const { done, value } = await reader.read();
|
| 154 |
+
if (done) break;
|
| 155 |
+
sourceBuffer.appendBuffer(value);
|
| 156 |
+
await new Promise(resolve => sourceBuffer.addEventListener('updateend', resolve, { once: true }));
|
| 157 |
+
}
|
| 158 |
+
mediaSource.endOfStream();
|
| 159 |
+
});
|
| 160 |
+
player.play();
|
| 161 |
+
};
|
| 162 |
+
}
|
| 163 |
+
</script>
|
| 164 |
+
</body>
|
| 165 |
+
</html>
|