trretretret's picture
basic
26ab438
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 };