Spaces:
Sleeping
Sleeping
| import { appModel } from './model.js'; | |
| import {utils} from './utils.js'; | |
| import {controller } from './controller.js' | |
| const view = { | |
| elem: { | |
| currentInputElem: null, | |
| voiceButton: null, | |
| }, | |
| recorder: null, | |
| async init() { | |
| this.recorder = new utils.Recorder(); | |
| this.elem.voiceButton = this.createButton(); | |
| appModel.keepButtonAliveInterval = setInterval(() => { | |
| const whisperButton = document.getElementById("whisper_voice_button"); | |
| if (!whisperButton) { | |
| this.createButton(); | |
| } | |
| }, 2000); | |
| }, | |
| createButton() { | |
| if (document.getElementById("whisper_voice_button")) { | |
| return; | |
| } | |
| let button = document.createElement("button"); | |
| this.elem.voiceButton = button; | |
| button.id = appModel.voice_button_id; | |
| button.innerText = "◯"; | |
| button.type = "button"; | |
| button.classList.add("speech-to-text-button"); | |
| button.style.top = window.innerHeight - 100 + "px"; | |
| button.style.left = "0"; | |
| button.style.width = "40px"; | |
| button.style.height = button.style.width; | |
| button.style.fontSize = "30px"; | |
| button.style.padding = "0"; | |
| button.style.border = "0px"; | |
| button.style.color = "blue"; | |
| button.style.background = "transparent"; | |
| button.style.zIndex = 1000000; | |
| button.style.position = "fixed"; | |
| button.style.borderRadius = "50%"; | |
| button.style.userSelect = "none"; | |
| button.style.touchAction = "none"; | |
| document.body.appendChild(button); | |
| utils.dragElement(button, button); | |
| button.addEventListener("click", () => { | |
| console.log("createButton():clicked"); | |
| }); | |
| button.addEventListener("pointerdown", async (event) => { | |
| event.preventDefault(); | |
| controller.handleRecording(event); | |
| }); | |
| button.addEventListener("pointerup", () => { | |
| console.log("createButton pointerup"); | |
| controller.stopRecording(); | |
| }); | |
| utils.addEventListenerForActualClick(button, (event) => { | |
| let clientX = event?.clientX; | |
| let clientY = event?.clientY; | |
| this.createMenu(clientX + 50, clientY + 50); | |
| }); | |
| utils.addEventListenerForActualClick(document.body, (event) => { | |
| if (view.recorder.isRecording) return; | |
| if (event.target.tagName === "INPUT" && | |
| appModel.supportedInputTypeList.includes(event.target.type)) { | |
| utils.moveElementNearMouse(button, event.target, true, event); | |
| } else if (event.target.tagName === "TEXTAREA" || | |
| utils.isEditableElement(event.target)) { | |
| utils.moveElementNearMouse(button, event.target, true, event); | |
| } | |
| }); | |
| window.addEventListener("resize", () => { | |
| let buttonPos = button.getBoundingClientRect(); | |
| if (buttonPos.top > window.innerHeight - buttonPos.height) { | |
| button.style.top = window.innerHeight - buttonPos.height + "px"; | |
| } | |
| }); | |
| return button; | |
| }, | |
| createMenu(x, y, id = "webai_input_menu") { | |
| const windowWidth = window.innerWidth; | |
| const windowHeight = window.innerHeight; | |
| let menuContainer = document.getElementById(id); | |
| if (menuContainer) { | |
| menuContainer.style.left = | |
| Math.min(x, windowWidth - menuContainer.offsetWidth * 0.5) + "px"; | |
| menuContainer.style.top = | |
| Math.min(y, windowHeight - menuContainer.offsetHeight) - 100 + "px"; | |
| menuContainer.style.zIndex = "99999"; | |
| return; | |
| } | |
| menuContainer = document.createElement("div"); | |
| document.body.appendChild(menuContainer); | |
| menuContainer.id = id; | |
| menuContainer.style.zIndex = "99999"; | |
| menuContainer.style.position = "fixed"; | |
| menuContainer.style.backgroundColor = "white"; | |
| menuContainer.style.boxShadow = "0 2px 5px rgba(0, 0, 0, 0.1)"; | |
| menuContainer.style.borderRadius = "4px"; | |
| menuContainer.style.display = "flex"; | |
| menuContainer.style.flexDirection = "column"; | |
| menuContainer.style.alignItems = "flex-start"; | |
| menuContainer.style.padding = "10px"; | |
| menuContainer.style.left = | |
| Math.min(x, windowWidth - menuContainer.offsetWidth) + "px"; | |
| menuContainer.style.top = | |
| Math.min(y, windowHeight - menuContainer.offsetHeight) - 100 + "px"; | |
| menuContainer.style.opacity = '0.7' | |
| utils.disableSelect(menuContainer); | |
| menuContainer.style.maxHeight = "60vh"; | |
| menuContainer.style.overflowY = "auto"; | |
| menuContainer.style.cssText += ` | |
| scrollbar-width: thin; | |
| scrollbar-color: rgba(0, 0, 0, 0.3) transparent; | |
| `; | |
| menuContainer.style.msOverflowStyle = "none"; | |
| function createMenuItem(textContent, handler) { | |
| const menuItem = document.createElement("button"); | |
| utils.makeButtonFeedback(menuItem); | |
| menuContainer.appendChild(menuItem); | |
| menuItem.style.cssText = ` | |
| background-color: white; | |
| border: none; | |
| font-size: 14px; | |
| width: 80px; | |
| text-align: left; | |
| cursor: pointer; | |
| margin-bottom: 0; | |
| margin-top: 0; | |
| padding: 5px 10px; | |
| color: #333; | |
| transition: background-color 0.3s ease; | |
| display: flex; | |
| align-items: center; | |
| justify-content: flex-start; | |
| border-radius: 4px; | |
| `; | |
| menuItem.textContent = textContent; | |
| menuItem.addEventListener("pointerdown", (event) => { | |
| event.preventDefault(); | |
| if (handler) { | |
| handler(); | |
| } | |
| }); | |
| return menuItem; | |
| } | |
| const removeMenuItem = createMenuItem("Remove Menu"); | |
| removeMenuItem.addEventListener("pointerdown", () => | |
| menuContainer.remove() | |
| ); | |
| menuContainer.appendChild(removeMenuItem); | |
| const closeButton = createMenuItem("Close"); | |
| closeButton.addEventListener("pointerdown", () => { | |
| if (confirm("remove the AI tool now?")) { | |
| clearInterval(appModel.keepButtonAliveInterval); | |
| view.elem.voiceButton.remove(); | |
| menuContainer.remove(); | |
| } | |
| }); | |
| menuContainer.appendChild(closeButton); | |
| createMenuItem("TTS", () => { | |
| let currentLineString = utils.getCurrentLineString(document.activeElement); | |
| let selectText = window.getSelection().toString(); | |
| let ttstext=selectText.length >= 1 ? selectText : currentLineString; | |
| utils.tts( | |
| ttstext, | |
| "de-DE-SeraphinaMultilingualNeural" | |
| ); | |
| }); | |
| const startMenuItem = createMenuItem("Start"); | |
| menuContainer.appendChild(startMenuItem); | |
| startMenuItem.addEventListener("pointerdown", () => { | |
| view.elem.voiceButton.style.top = '0px'; | |
| view.elem.voiceButton.style.left = window.innerWidth * 0.8 + 'px'; | |
| appModel.isRecording = true; | |
| controller.startRecordingWithSilenceDetection(); | |
| }); | |
| let copyButton = createMenuItem("Copy"); | |
| menuContainer.appendChild(copyButton); | |
| copyButton.addEventListener("pointerdown", (e) => { | |
| e.preventDefault(); | |
| if(window.getSelection().toString().length > 0){ | |
| document.execCommand("copy") ; | |
| } | |
| else{ | |
| utils.copyToClipboard(utils.getCurrentLineString(document.activeElement)); | |
| } | |
| utils.showToast("Copied to clipboard"); | |
| }); | |
| createMenuItem("Cut", () => { | |
| document.execCommand("copy"); | |
| document.execCommand("delete"); | |
| utils.showToast("Cut to clipboard"); | |
| }); | |
| let pasteButton = createMenuItem("Paste"); | |
| menuContainer.appendChild(pasteButton); | |
| pasteButton.addEventListener("pointerdown", async (e) => { | |
| e.preventDefault(); | |
| try { | |
| const text = await navigator.clipboard.readText(); | |
| utils.writeText(document.activeElement, text); | |
| console.log("Clipboard text:", text); | |
| } catch (err) { | |
| console.error("Clipboard access denied:", err); | |
| } | |
| }); | |
| let enterButton = createMenuItem("Enter"); | |
| menuContainer.appendChild(enterButton); | |
| enterButton.addEventListener("pointerdown", (e) => { | |
| e.preventDefault(); | |
| document.execCommand("insertText", false, "\n"); | |
| }); | |
| createMenuItem("Correct", () => { | |
| let correctPrompt = | |
| 'correct mistakes of the text, put the corrected text in the codeblock:\n '; | |
| controller.chat(correctPrompt); | |
| }); | |
| let askButton = createMenuItem("Ask"); | |
| askButton.style.touchAction='none'; | |
| menuContainer.appendChild(askButton); | |
| askButton.addEventListener("pointerdown", (e) => { | |
| e.preventDefault(); | |
| controller.ask(); | |
| document.body.addEventListener("pointerup", () => { | |
| controller.stopRecording(); | |
| }, { once: true }); | |
| }); | |
| menuContainer.style.left = | |
| Math.min(x, windowWidth - menuContainer.offsetWidth * 0.5) + "px"; | |
| menuContainer.style.top = | |
| Math.min(y, windowHeight - menuContainer.offsetHeight) - 100 + "px"; | |
| document.body.appendChild(menuContainer); | |
| }, | |
| }; | |
| export { view }; |