From a99d45da3319d9cc99de52b219384eceb4c4090d Mon Sep 17 00:00:00 2001 From: mikejgray Date: Fri, 22 Dec 2023 00:07:36 -0600 Subject: [PATCH] feat: animations by audio recording status --- neon_iris/models/web_sat.py | 2 +- neon_iris/static/scripts/main.js | 0 neon_iris/static/scripts/sprite.js | 20 +++++++++ neon_iris/static/scripts/ui.js | 2 + neon_iris/static/scripts/websocket.js | 3 ++ neon_iris/static/sprite.css | 61 +++++++++++++++++++++++++++ neon_iris/static/styles.css | 4 ++ neon_iris/templates/index.html | 5 ++- 8 files changed, 95 insertions(+), 2 deletions(-) delete mode 100644 neon_iris/static/scripts/main.js create mode 100644 neon_iris/static/scripts/sprite.js create mode 100644 neon_iris/static/sprite.css diff --git a/neon_iris/models/web_sat.py b/neon_iris/models/web_sat.py index 87ad757..08ab5ef 100644 --- a/neon_iris/models/web_sat.py +++ b/neon_iris/models/web_sat.py @@ -1,7 +1,7 @@ """API data models for the WebSAT API.""" # NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System # All trademark and other rights reserved by their respective owners -# Copyright 2008-2021 Neongecko.com Inc. +# Copyright 2008-2024 Neongecko.com Inc. # BSD-3 # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: diff --git a/neon_iris/static/scripts/main.js b/neon_iris/static/scripts/main.js deleted file mode 100644 index e69de29..0000000 diff --git a/neon_iris/static/scripts/sprite.js b/neon_iris/static/scripts/sprite.js new file mode 100644 index 0000000..303e72b --- /dev/null +++ b/neon_iris/static/scripts/sprite.js @@ -0,0 +1,20 @@ +const sprite = document.querySelector(".sprite"); + +function triggerWake() { + sprite.classList.remove("record", "waiting"); + sprite.classList.add("wake"); +} + +function triggerRecord() { + sprite.classList.remove("wake", "waiting"); + sprite.classList.add("record"); +} + +function triggerWaiting() { + sprite.classList.remove("wake", "record"); + sprite.classList.add("waiting"); +} + +function triggerDone() { + sprite.classList.remove("wake", "record", "waiting"); +} diff --git a/neon_iris/static/scripts/ui.js b/neon_iris/static/scripts/ui.js index 8730434..abbcbb2 100644 --- a/neon_iris/static/scripts/ui.js +++ b/neon_iris/static/scripts/ui.js @@ -18,6 +18,7 @@ function submitMessage() { async function getAIResponse(text = "", recording = "") { try { + triggerWaiting(); // Trigger waiting animation const payload = text !== "" && recording === "" ? { utterance: text } @@ -46,6 +47,7 @@ async function getAIResponse(text = "", recording = "") { // Add in the user's transcription if STT if (text === "" && recording !== "") { const userMessage = createMessageDiv("user", data.utterance); + triggerDone(); // Trigger done animation appendMessageToHistory(userMessage); saveMessageToLocalStorage("user", data.utterance); } diff --git a/neon_iris/static/scripts/websocket.js b/neon_iris/static/scripts/websocket.js index 6ff963b..3e56b8a 100644 --- a/neon_iris/static/scripts/websocket.js +++ b/neon_iris/static/scripts/websocket.js @@ -83,6 +83,7 @@ async function handleSpeechEnd(audio) { downloadLink.download = "recorded_audio.wav"; downloadLink.textContent = "Download Recorded Audio"; downloadArea.appendChild(downloadLink); + triggerWaiting(); // Trigger waiting animation } else { console.error("Download area not found"); } @@ -131,6 +132,7 @@ const WebSocketHandler = (() => { audio.onended = () => { console.log("Activation sound is done playing"); if (myVad && !isVadRunning) { + triggerRecord(); // Trigger recording animation myVad.start(); isVadRunning = true; } else if (!shouldListen && isVadRunning) { @@ -138,6 +140,7 @@ const WebSocketHandler = (() => { isVadRunning = false; } }; + triggerWake(); // Trigger wake animation audio.play(); lastActivationTime = currentTime; } diff --git a/neon_iris/static/sprite.css b/neon_iris/static/sprite.css new file mode 100644 index 0000000..73d5d1e --- /dev/null +++ b/neon_iris/static/sprite.css @@ -0,0 +1,61 @@ +/* Base styles for the split sprite */ +.sprite { + width: 40px; /* sprite width adjusted to 40px */ + height: 40px; /* sprite height adjusted to 40px */ + border-radius: 50%; + background-image: linear-gradient(to right, #000 50%, #fff 50%); + box-shadow: 0 0 0 2px #000; /* Adjusted border thickness for smaller size */ + transition: transform 0.3s ease, opacity 0.3s ease; + opacity: 0; /* sprite is invisible by default */ + position: relative; /* Required for absolute positioning of pseudo-elements */ + display: flex; /* Center content */ + justify-content: center; + align-items: center; + margin: 20px; +} + +/* Pulse animation while recording */ +@keyframes pulse { + 0%, + 100% { + transform: scale(1); + } + 50% { + transform: scale(1.1); + } +} + +.sprite.record { + animation: pulse 1s infinite ease-in-out; + opacity: 1; /* sprite is visible while recording */ +} + +/* Spin animation while waiting for a response */ +@keyframes spin { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} + +.sprite.waiting { + animation: spin 2s infinite linear; + opacity: 1; /* sprite is visible while waiting */ +} + +/* Appear animation for wake word activation */ +@keyframes appear { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } +} + +.sprite.wake { + animation: appear 1s forwards; + opacity: 1; /* sprite is visible when awake */ +} diff --git a/neon_iris/static/styles.css b/neon_iris/static/styles.css index 64ea000..17d20a9 100644 --- a/neon_iris/static/styles.css +++ b/neon_iris/static/styles.css @@ -7,6 +7,10 @@ html { margin: 0; padding: 0; } +img.logo { + width: 40px; + height: 40px; +} a { text-decoration: none; } diff --git a/neon_iris/templates/index.html b/neon_iris/templates/index.html index 6969c90..ddb610a 100644 --- a/neon_iris/templates/index.html +++ b/neon_iris/templates/index.html @@ -6,11 +6,12 @@ {{ title }} +
- {{ description }} + {{ description }}
@@ -22,6 +23,7 @@
+
@@ -36,4 +38,5 @@ +