diff --git a/src/main.js b/src/main.js index 5547a40e430a4e3a0bc1b415201adcbc3366dca5..85d6f1fa4b0121ce27fdbbe2f2d405787100c718 100644 --- a/src/main.js +++ b/src/main.js @@ -5,8 +5,8 @@ const config = { controls: { mouse: true, button: true, - voice: false, - motion: true, + voice: true, + motion: false, }, }; @@ -47,9 +47,9 @@ const navigation = { }, }, check: { - label: "Check", + label: "Checklist", path: "/menu/check", - command: "check", + command: "checklist", submenu: { daily: { label: "Daily", @@ -367,6 +367,15 @@ function focusClosestItem(targetY = window.innerHeight / 2) { return focusNavigationItem(closestIndex); } +// deselect all items +function deselectAllItems() { + const navigationList = document.querySelectorAll("#navigation li"); + for (let item of navigationList) { + item.querySelector(".progress").style.animation = ""; + delete item.dataset.selected; + } +} + // moving up navigation tree function moveUpNavigationLevel() { const path = window.location.pathname; @@ -403,14 +412,7 @@ function addMouseControls() { // unfocus all items appEl.addEventListener("mouseout", (event) => { const listItem = event.target.closest("li.link"); - if (listItem) { - // unselect all items - const navigationList = document.querySelectorAll("#navigation li"); - for (let item of navigationList) { - item.querySelector(".progress").style.animation = ""; - delete item.dataset.selected; - } - } + if (listItem) deselectAllItems(); }); // select on mouseclick @@ -527,13 +529,12 @@ function addVoiceControls() { SpeechRecognitionEvent || webkitSpeechRecognitionEvent; let availableCommands = []; - let commands = []; - updateVoiceCommandList(); + let commands = getVoiceCommandList(); // listen for menu updates and refresh commands list const navigationEl = document.getElementById("navigation"); navigationEl.addEventListener("updated", (event) => { - updateVoiceCommandList(); + commands = getVoiceCommandList(); }); let recognition = new SpeechRecognition(); @@ -557,12 +558,12 @@ function addVoiceControls() { recognition.onresult = function (event) { let command = event.results[0][0].transcript.toLowerCase(); let confidence = event.results[0][0].confidence; - command = command.toLowerCase(); console.log(`Voice > Received "${command}" (${confidence.toFixed(2)})`); - if (commands.includes(command)) { - let index = commands.indexOf(command); // get index of command in array + const matchingItem = commands.find(item => command.includes(item)); + if (matchingItem) { + let index = commands.indexOf(matchingItem); // get index of command in array let path = availableCommands[index].path; // get corresponding path in availableCommands array navigateToPath(path); } else if (command.includes("select") || command.includes("okay")) @@ -588,7 +589,7 @@ function addVoiceControls() { }; // updating voice command list - function updateVoiceCommandList() { + function getVoiceCommandList() { // search and save currently available voice commands availableCommands = []; const listItems = document.querySelectorAll("a[data-command]"); @@ -601,7 +602,7 @@ function addVoiceControls() { } // form commands array for grammarlist - commands = availableCommands.map((x) => x.command); + return availableCommands.map((x) => x.command); } } @@ -613,7 +614,8 @@ function addMotionControls() { let screenCenter = new Point(window.innerWidth / 2, window.innerHeight / 2); let idleTimeout; let intervalId; - let idleDelay = 3000; //idle time before reseting reference point + let intervalRunning = false; + let idleDelay = 5000; //idle time before reseting reference point let autoSelect = false; // selecting items with time delay let drawMotionFlag = true; // draw motion values for debugging @@ -624,7 +626,7 @@ function addMotionControls() { const bufferIterationLimit = 5; // iterations before new average calculation is triggered const bufferLength = 10; // length of value buffer const noiseThreshold = 0.02; // threshold to avoid general sensor noise - const inactiveThreshold = 10; // threshold to define inactive area in the reference + const inactiveThreshold = 50; // threshold to define inactive area in the reference // create canvas element for motion visualization const canvasEl = document.createElement("canvas"); @@ -690,52 +692,62 @@ function addMotionControls() { // clear timeout monitoring if pointer is within reasonable distance if (distanceToReference < 400) { clearTimeout(idleTimeout); - clearInterval(intervalId); // reseting reference point after a delay of holding still - idleTimeout = setTimeout(() => { - printLogMsg("restart timer"); - // start approaching new reference point - let incrementValue = 0.01; - intervalId = setInterval(() => { - if (referenceY < averageY) { - printLogMsg("down"); - referenceY += incrementValue; - if (referenceY >= averageY) { - printLogMsg("clear interval down"); - referenceY = averageY; // Ensure exact match with target - clearInterval(intervalId); - } - } else if (referenceY > averageY) { - printLogMsg("up"); - referenceY -= incrementValue; - if (referenceY <= averageY) { - printLogMsg("clear interval up"); - referenceY = averageY; // Ensure exact match with target - clearInterval(intervalId); - } - } else { - printLogMsg("clear interval neutral"); - clearInterval(intervalId); - } - }, 100); - focusClosestItem(referenceY); - }, idleDelay); + if (!intervalRunning) + idleTimeout = setTimeout(() => { + printLogMsg("starting timer"); + // start approaching new reference point + let incrementValue = 0.01; + intervalId = setInterval(() => { + intervalRunning = true; + if (referenceY < averageY) { + printLogMsg("down"); + referenceY += incrementValue; + if (referenceY >= averageY) { + referenceY = averageY; // Ensure exact match with target + stopInterval(intervalId); + } + } else if (referenceY > averageY) { + printLogMsg("up"); + referenceY -= incrementValue; + if (referenceY <= averageY) { + referenceY = averageY; // Ensure exact match with target + stopInterval(intervalId); + } + } else stopInterval(intervalId); + }, 100); + }, idleDelay); } - lastAverageY = averageY; // save averageY for later comparison - - let itemEl = focusClosestItem(targetY); - - if (autoSelect) { - let progressEl = itemEl.querySelector(".progress"); - progressEl.style.animation = "progress 3s ease-out forwards"; + // select item if ouside of center save area + if (distanceToReference > inactiveThreshold) { + let targetY = averageY; + targetY = (targetY - referenceY) * -sensitivity; + let itemEl = focusClosestItem(targetY); + + // trigger autoselection via motion + if (autoSelect) { + let progressEl = itemEl.querySelector(".progress"); + progressEl.style.animation = "progress 2s ease-out forwards"; + } + } else { + printLogMsg("deselect all"); + deselectAllItems(); } + + lastAverageY = averageY; // save averageY for later comparison } bufferIterations = 0; } bufferIterations++; } + + function stopInterval(id) { + printLogMsg("interval stopped"); + clearInterval(id); + intervalRunning = false; + } } function drawMotion(y) { @@ -744,8 +756,6 @@ function addMotionControls() { const pointSize = 10; ctx.clearRect(0, 0, canvasEl.width, canvasEl.height); - ctx.strokeStyle = "rgba(255, 0, 255, .7)"; - ctx.lineWidth = 5; ctx.beginPath(); ctx.arc( @@ -755,16 +765,24 @@ function addMotionControls() { 0, 2 * Math.PI ); + ctx.strokeStyle = "rgba(255, 0, 255, .6)"; + ctx.lineWidth = 3; + ctx.stroke(); + ctx.closePath(); + + ctx.beginPath(); ctx.moveTo(screenCenter.x, screenCenter.y); ctx.lineTo(screenCenter.x, screenCenter.y + y); + ctx.strokeStyle = "rgba(255, 0, 255, .6)"; + ctx.lineWidth = 3; ctx.stroke(); ctx.closePath(); ctx.beginPath(); ctx.arc(screenCenter.x, screenCenter.y + y, pointSize, 0, 2 * Math.PI); + ctx.fillStyle = "rgba(255, 0, 255, .6)"; ctx.fill(); - ctx.fillStyle = "rgb(255, 0, 255)"; - ctx.stroke(); + ctx.closePath(); } } }