Skip to content

Commit a8ea4e1

Browse files
authored
Update joinin.html
1 parent ee42b10 commit a8ea4e1

1 file changed

Lines changed: 185 additions & 12 deletions

File tree

joinin.html

Lines changed: 185 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -338,17 +338,29 @@
338338
<p>Tap “Connect” to connect to the bridge and TeamTalk.</p>
339339
</div>
340340

341-
<div id="input-area">
342-
<input id="chat-input"
343-
type="text"
344-
placeholder="Type your message…"
345-
aria-label="Chat message"
346-
onkeydown="if(event.key==='Enter'){ sendTeamTalkChat(); }">
347-
<button id="send-btn"
348-
onclick="sendTeamTalkChat()">
349-
Send
350-
</button>
351-
</div>
341+
<div id="input-area">
342+
<input id="chat-input" type="text" placeholder="Type a message…">
343+
344+
<button onclick="sendTeamTalkChat()">Send</button>
345+
346+
<div id="mode-status"
347+
aria-live="polite"
348+
style="font-size: 0.9rem; margin-top: 0.25rem;">
349+
Messages will be sent in the mode the system thinks is least effort.
350+
</div>
351+
352+
<button id="toggle-speech-button"
353+
aria-pressed="false"
354+
onclick="toggleSpeech()"
355+
style="margin-top: 0.5rem;">
356+
Toggle Speech
357+
</button>
358+
<button id="ptt-button"
359+
aria-pressed="false"
360+
style="margin-top: 0.5rem;">
361+
Hold to talk
362+
</button>
363+
</div>
352364

353365
<div id="async-thread" style="
354366
display: none;
@@ -433,7 +445,168 @@ <h2 id="users-heading">Users</h2>
433445
<script src="bridge.js"></script>
434446

435447
<script>
436-
window.addEventListener("DOMContentLoaded", () => {
448+
449+
<script>
450+
// -------------------------------
451+
// Text send with automation
452+
// -------------------------------
453+
window.sendTeamTalkChat = function () {
454+
const input = document.getElementById("chat-input");
455+
if (!input) return;
456+
const text = input.value.trim();
457+
if (!text) return;
458+
459+
const modeUsed = bridge.autoSend(text);
460+
461+
const modeStatus = document.getElementById("mode-status");
462+
if (modeStatus) {
463+
modeStatus.textContent =
464+
modeUsed === "live"
465+
? "Sent as live message"
466+
: "Sent as async message";
467+
}
468+
469+
input.value = "";
470+
};
471+
472+
// -------------------------------
473+
// Render live and async streams
474+
// -------------------------------
475+
bridge.on("chat-message", (msg) => {
476+
const chat = document.getElementById("chat");
477+
if (!chat) return;
478+
const p = document.createElement("p");
479+
p.textContent = `${msg.user}: ${msg.text}`;
480+
chat.appendChild(p);
481+
chat.scrollTop = chat.scrollHeight;
482+
});
483+
484+
bridge.on("async-message", (msg) => {
485+
const asyncThread = document.getElementById("async-thread");
486+
if (!asyncThread) return;
487+
const div = document.createElement("div");
488+
const time = new Date(msg.timestamp || Date.now()).toLocaleTimeString();
489+
div.textContent = `${time}${msg.text}`;
490+
asyncThread.appendChild(div);
491+
asyncThread.scrollTop = asyncThread.scrollHeight;
492+
});
493+
494+
// -------------------------------
495+
// Dwell helper for symbols
496+
// -------------------------------
497+
function attachDwell(element, symbolId, label) {
498+
let dwellTimer = null;
499+
const DWELL_MS = 800; // tune this
500+
501+
element.addEventListener("pointerdown", () => {
502+
dwellTimer = setTimeout(() => {
503+
bridge.sendDwellSymbol(symbolId, label);
504+
}, DWELL_MS);
505+
});
506+
507+
const cancel = () => {
508+
if (dwellTimer) {
509+
clearTimeout(dwellTimer);
510+
dwellTimer = null;
511+
}
512+
};
513+
514+
element.addEventListener("pointerup", cancel);
515+
element.addEventListener("pointerleave", cancel);
516+
}
517+
518+
// You can call attachDwell(buttonEl, symbolId, label) when creating symbol buttons.
519+
520+
// -------------------------------
521+
// Mic capture + PTT for live audio
522+
// -------------------------------
523+
let mediaStream = null;
524+
let mediaRecorder = null;
525+
let isRecording = false;
526+
527+
async function initMic() {
528+
if (mediaStream) return;
529+
try {
530+
mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true });
531+
} catch (err) {
532+
console.error("Mic error:", err);
533+
alert("Could not access microphone.");
534+
}
535+
}
536+
537+
function startLiveAudio() {
538+
if (isRecording) return;
539+
if (!mediaStream) {
540+
initMic().then(() => {
541+
if (mediaStream) startLiveAudio();
542+
});
543+
return;
544+
}
545+
546+
mediaRecorder = new MediaRecorder(mediaStream, {
547+
mimeType: "audio/webm;codecs=opus"
548+
});
549+
550+
mediaRecorder.ondataavailable = (e) => {
551+
if (!e.data || !e.data.size) return;
552+
const reader = new FileReader();
553+
reader.onloadend = () => {
554+
const base64Data = btoa(
555+
String.fromCharCode(...new Uint8Array(reader.result))
556+
);
557+
bridge.sendLiveAudioChunk(base64Data);
558+
};
559+
reader.readAsArrayBuffer(e.data);
560+
};
561+
562+
mediaRecorder.start(250); // send chunk every 250ms
563+
bridge.startLiveAudioSession();
564+
isRecording = true;
565+
}
566+
567+
function stopLiveAudio() {
568+
if (!isRecording) return;
569+
if (mediaRecorder && mediaRecorder.state !== "inactive") {
570+
mediaRecorder.stop();
571+
}
572+
bridge.stopLiveAudioSession();
573+
isRecording = false;
574+
}
575+
576+
const pttButton = document.getElementById("ptt-button");
577+
if (pttButton) {
578+
pttButton.addEventListener("pointerdown", () => {
579+
pttButton.setAttribute("aria-pressed", "true");
580+
startLiveAudio();
581+
});
582+
583+
const stop = () => {
584+
pttButton.setAttribute("aria-pressed", "false");
585+
stopLiveAudio();
586+
};
587+
588+
pttButton.addEventListener("pointerup", stop);
589+
pttButton.addEventListener("pointerleave", stop);
590+
let isSpeechToggled = false;
591+
592+
function toggleSpeech() {
593+
if (!isSpeechToggled) {
594+
// Turn ON continuous transmit
595+
isSpeechToggled = true;
596+
startLiveAudio();
597+
const btn = document.getElementById("toggle-speech-button");
598+
if (btn) btn.setAttribute("aria-pressed", "true");
599+
} else {
600+
// Turn OFF continuous transmit
601+
isSpeechToggled = false;
602+
stopLiveAudio();
603+
const btn = document.getElementById("toggle-speech-button");
604+
if (btn) btn.setAttribute("aria-pressed", "false");
605+
}
606+
}
607+
</script>
608+
609+
window.addEventListener("DOMContentLoaded", () => {
437610

438611
/* ---------------------------------------------------------
439612
NICKNAME RESOLUTION MODULE

0 commit comments

Comments
 (0)