Spaces:
Sleeping
Sleeping
| const utils = { | |
| Recorder: class Recorder { | |
| constructor() { | |
| this.isRecording = false; | |
| this.mediaRecorder; | |
| this.encodeType = "audio/mpeg"; | |
| this.language = "en"; | |
| this.recordingColor = "lightblue"; | |
| this.autoStop=true; | |
| } | |
| async startRecording( | |
| targetElement, | |
| silenceHandler = () => { | |
| console.log("silence detect"); | |
| }, | |
| autoStop = true | |
| ) { | |
| targetElement = targetElement || document.querySelector(`#whisper_voice_button`); | |
| this.stopRecording(); | |
| console.log("start recording"); | |
| return navigator.mediaDevices | |
| .getUserMedia({ audio: true }) | |
| .then((stream) => { | |
| this.mediaRecorder = new MediaRecorder(stream); | |
| let silenceStart = Date.now(); | |
| let silenceDuration = 0; | |
| let mediaRecorder = this.mediaRecorder; | |
| let audioChunks = []; | |
| mediaRecorder.start(); | |
| this.isRecording = true; | |
| targetElement.style.backgroundColor = "rgba(173, 216, 230, 0.3)"; | |
| let volumeInterval; | |
| let audioContext; | |
| audioContext = new AudioContext(); | |
| const analyser = audioContext.createAnalyser(); | |
| const microphone = audioContext.createMediaStreamSource( | |
| mediaRecorder.stream | |
| ); | |
| microphone.connect(analyser); | |
| analyser.fftSize = 512; | |
| const bufferLength = analyser.frequencyBinCount; | |
| const dataArray = new Uint8Array(bufferLength); | |
| const updateButtonFontSize = () => { | |
| analyser.getByteFrequencyData(dataArray); | |
| let sum = 0; | |
| for (let i = 0; i < bufferLength; i++) { | |
| sum += dataArray[i]; | |
| } | |
| let averageVolume = sum / bufferLength; | |
| if (averageVolume < 10) { | |
| silenceDuration = Date.now() - silenceStart; | |
| if (silenceDuration > 1000) { | |
| silenceHandler(); | |
| } | |
| } else { | |
| silenceStart = Date.now(); | |
| } | |
| let scale = 3 + averageVolume / 15; | |
| targetElement.style.transform = `scale(${scale})`; | |
| }; | |
| volumeInterval = setInterval(updateButtonFontSize, 100); | |
| mediaRecorder.addEventListener("dataavailable", (event) => { | |
| console.log("dataavailable"); | |
| audioChunks.push(event.data); | |
| }); | |
| return new Promise((resolve, reject) => { | |
| mediaRecorder.addEventListener("stop", async () => { | |
| this.isRecording = false; | |
| console.log("stop"); | |
| clearInterval(volumeInterval); | |
| const audioBlob = new Blob(audioChunks, { | |
| type: this.encodeType, | |
| }); | |
| targetElement.style.transform = `scale(1)`; | |
| targetElement.style.background = "transparent"; | |
| audioContext?.close(); | |
| mediaRecorder.stream.getTracks().forEach((track) => track.stop()); | |
| console.log("resolved "); | |
| resolve(audioBlob); | |
| }); | |
| }); | |
| }) | |
| .catch((error) => { | |
| if ( | |
| error.name === "PermissionDeniedError" || | |
| error.name === "NotAllowedError" | |
| ) { | |
| console.error("User denied permission to access audio"); | |
| console.log("Audio permission denied"); | |
| } else { | |
| console.error( | |
| "An error occurred while accessing the audio device", | |
| error | |
| ); | |
| } | |
| }); | |
| } | |
| async startRecordingWithSilenceDetection( | |
| targetElement,silenceHandler = () => console.log("silence detect")) { | |
| let autoStop = this.autoStop || true; | |
| this.stopRecording(); | |
| console.log("start recording"); | |
| return navigator.mediaDevices | |
| .getUserMedia({ audio: true }) | |
| .then((stream) => { | |
| this.mediaRecorder = new MediaRecorder(stream); | |
| let startTime = Date.now(); | |
| let isSilent = false; | |
| let isLongSilent = true; | |
| let silenceStart = Date.now(); | |
| let silenceDuration = 0; | |
| let mediaRecorder = this.mediaRecorder; | |
| let audioChunks = []; | |
| mediaRecorder.start(); | |
| this.isRecording = true; | |
| targetElement.style.backgroundColor = "rgba(173, 216, 230, 0.3)"; | |
| let volumeInterval; | |
| let audioContext; | |
| audioContext = new AudioContext(); | |
| const analyser = audioContext.createAnalyser(); | |
| const microphone = audioContext.createMediaStreamSource( | |
| mediaRecorder.stream | |
| ); | |
| microphone.connect(analyser); | |
| analyser.fftSize = 512; | |
| const bufferLength = analyser.frequencyBinCount; | |
| const dataArray = new Uint8Array(bufferLength); | |
| const handleAudioData = () => { | |
| analyser.getByteFrequencyData(dataArray); | |
| let sum = 0; | |
| for (let i = 0; i < bufferLength; i++) { | |
| sum += dataArray[i]; | |
| } | |
| let averageVolume = sum / bufferLength; | |
| if (averageVolume < 15) { | |
| if (isSilent) { | |
| silenceDuration = Date.now() - silenceStart; | |
| if (silenceDuration > 3000) { | |
| isLongSilent = true; | |
| mediaRecorder.requestData(); | |
| silenceStart = Date.now(); | |
| } | |
| } else { | |
| silenceDuration = Date.now() - silenceStart; | |
| if (silenceDuration > 1000) { | |
| isSilent = true; | |
| console.log('change isSilent to true'); | |
| mediaRecorder.requestData(); | |
| } | |
| } | |
| } else { | |
| isSilent = false; | |
| isLongSilent = false; | |
| silenceStart = Date.now(); | |
| } | |
| let scale = 3 + averageVolume / 15; | |
| targetElement.style.transform = `scale(${scale})`; | |
| }; | |
| volumeInterval = setInterval(handleAudioData, 100); | |
| let counter = 0; | |
| let firstdata; | |
| setTimeout(() => { | |
| mediaRecorder.requestData(); | |
| }, 200); | |
| mediaRecorder.addEventListener("dataavailable", (event) => { | |
| if (autoStop === true) { | |
| if (Date.now() - startTime > 10000) { | |
| mediaRecorder.stop(); | |
| } | |
| } | |
| counter++; | |
| if (counter <= 1) { | |
| firstdata = event.data; | |
| if (event.data.size > 0) { | |
| audioChunks.push(event.data); | |
| } | |
| return; | |
| } | |
| console.log("dataavailable", event.data); | |
| if (isLongSilent) { | |
| console.log("dataavailable,Long silent will do noting", event.data); | |
| return; | |
| } | |
| silenceHandler(new Blob([firstdata, event.data], { type: mediaRecorder.mimeType })); | |
| }); | |
| return new Promise((resolve, reject) => { | |
| mediaRecorder.addEventListener("stop", async () => { | |
| this.isRecording = false; | |
| console.log("stop"); | |
| clearInterval(volumeInterval); | |
| const audioBlob = new Blob(audioChunks, { | |
| type: this.encodeType, | |
| }); | |
| targetElement.style.transform = `scale(1)`; | |
| targetElement.style.background = "transparent"; | |
| audioContext?.close(); | |
| mediaRecorder.stream.getTracks().forEach((track) => track.stop()); | |
| console.log("resolved "); | |
| resolve(audioBlob); | |
| }); | |
| }); | |
| }) | |
| .catch((error) => { | |
| if ( | |
| error.name === "PermissionDeniedError" || | |
| error.name === "NotAllowedError" | |
| ) { | |
| console.error("User denied permission to access audio"); | |
| showNotification("Audio permission denied"); | |
| } else { | |
| console.error( | |
| "An error occurred while accessing the audio device", | |
| error | |
| ); | |
| showNotification("Error accessing audio device"); | |
| } | |
| }); | |
| } | |
| stopRecording() { | |
| this.isRecording = false; | |
| this.mediaRecorder?.stop(); | |
| this.mediaRecorder?.audioContext?.close(); | |
| this.mediaRecorder?.stream?.getTracks().forEach((track) => track.stop()); | |
| } | |
| }, | |
| tts: function synthesizeSpeech(text = 'test text', voice = 'alloy') { | |
| let url = `https://im1111-free-get-tts.hf.space/tts/${encodeURIComponent(text)}`; | |
| let container = document.getElementById("devlent_tts_container"); | |
| if (!container) { | |
| container = document.createElement("div"); | |
| container.id = "devlent_tts_container"; | |
| document.body.appendChild(container); | |
| } | |
| let audio = document.getElementById("tts_audio"); | |
| if (!audio) { | |
| audio = document.createElement("audio"); | |
| audio.id = "tts_audio"; | |
| container.appendChild(audio); | |
| let button = document.createElement("button"); | |
| button.innerHTML = "x"; | |
| button.style.backgroundColor = 'transparent'; | |
| button.style.marginLeft = '10px'; | |
| button.addEventListener('pointerdown', () => container.style.display = "none") | |
| let br = document.createElement('br'); | |
| container.prepend(br); | |
| container.prepend(button); | |
| } | |
| container.style.display = "block"; | |
| container.style.position = "fixed"; | |
| container.style.top = "20px"; | |
| container.style.right = "0"; | |
| container.style.height = 'fit-content' | |
| if (!text) return; | |
| audio.src = url; | |
| audio.controls = true; | |
| audio.autoplay = true; | |
| utils.dragElement(container, container) | |
| }, | |
| stt: async (audioBlob) => { | |
| const formData = new FormData(); | |
| formData.append("file", audioBlob, "audio.mp3"); | |
| formData.append("model", "whisper-large-v3"); | |
| try { | |
| const response = await fetch('/openai/v1/audio/transcriptions', { | |
| method: 'POST', | |
| body: formData | |
| }); | |
| if (!response.ok) { | |
| throw new Error(`Error: ${response.status} ${response.statusText}`); | |
| } | |
| const data = await response.json(); | |
| return data; | |
| } catch (error) { | |
| console.error('There was an error with the transcription request:', error); | |
| } | |
| }, | |
| checkValidString(str) { | |
| if (str === undefined || str === null || str.trim() === "") { | |
| return false; | |
| } | |
| if (str === "undefined" || str === "null") { | |
| return false; | |
| } | |
| return true; | |
| }, | |
| getCurrentLineString(element) { | |
| const selection = window.getSelection(); | |
| const range = selection.getRangeAt(0); | |
| const node = range.startContainer; | |
| const offset = range.startOffset; | |
| if (node.nodeType === Node.TEXT_NODE) { | |
| const text = node.textContent; | |
| const lineStart = text.lastIndexOf('\n', offset) + 1; | |
| const lineEnd = text.indexOf('\n', offset); | |
| const line = lineEnd === -1 ? text.slice(lineStart) : text.slice(lineStart, lineEnd); | |
| return line; | |
| } | |
| const walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT); | |
| let currentNode, currentLine = ''; | |
| while (currentNode = walker.nextNode()) { | |
| const text = currentNode.textContent; | |
| const lines = text.split('\n'); | |
| for (let i = 0; i < lines.length; i++) { | |
| if (range.intersectsNode(currentNode)) { | |
| currentLine = lines[i]; | |
| break; | |
| } | |
| } | |
| if (currentLine !== '') { | |
| break; | |
| } | |
| } | |
| return currentLine; | |
| }, | |
| getCursorPosition(element) { | |
| let caretOffset = 0; | |
| const doc = element.ownerDocument || element.document; | |
| const win = doc.defaultView || doc.parentWindow; | |
| let sel; | |
| if (typeof win.getSelection != "undefined") { | |
| sel = win.getSelection(); | |
| if (sel.rangeCount > 0) { | |
| const range = sel.getRangeAt(0); | |
| const preCaretRange = range.cloneRange(); | |
| preCaretRange.selectNodeContents(element); | |
| preCaretRange.setEnd(range.endContainer, range.endOffset); | |
| caretOffset = preCaretRange.toString().length; | |
| } | |
| } else if ((sel = doc.selection) && sel.type != "Control") { | |
| const textRange = sel.createRange(); | |
| const preCaretTextRange = doc.body.createTextRange(); | |
| preCaretTextRange.moveToElementText(element); | |
| preCaretTextRange.setEndPoint("EndToEnd", textRange); | |
| caretOffset = preCaretTextRange.text.length; | |
| } | |
| console.log('caretOffset:',caretOffset); | |
| return caretOffset; | |
| } | |
| , | |
| getCurrentBlock(elem) { | |
| elem=elem.parentElement; | |
| const cursorPosition = this.getCursorPosition(elem); | |
| const text = elem.innerText; | |
| let blockStart = text.indexOf('\n\n', cursorPosition) + 2; | |
| if (blockStart === 1) { // if we're at the start of the text | |
| blockStart = 0; | |
| } | |
| let blockEnd = text.lastIndexOf('\n\n', cursorPosition); | |
| if (blockEnd === -1) { // if we're at the end of the text | |
| blockEnd = text.length; | |
| } | |
| return text.substring(blockStart, blockEnd).trim(); | |
| } | |
| , | |
| isEditableElement: function isEditableElement(element) { | |
| while (element) { | |
| if (element.contentEditable === "true") { | |
| return true; | |
| } | |
| element = element.parentElement; | |
| } | |
| return false; | |
| }, | |
| disableSelect: function disableSelect(element) { | |
| element.style.userSelect = "none"; | |
| element.addEventListener("pointerdown", (e) => { | |
| e.preventDefault(); | |
| }); | |
| }, | |
| getSelectionText: function getSelectionText() { | |
| let activeElement = document.activeElement; | |
| if (activeElement && activeElement.value) { | |
| return activeElement.value.substring( | |
| activeElement.selectionStart, | |
| activeElement.selectionEnd | |
| ); | |
| } else { | |
| return window.getSelection().toString(); | |
| } | |
| }, | |
| makeButtonFeedback: function makeButtonFeedback(button) { | |
| let originalColor = button.style.backgroundColor || "white"; | |
| button.addEventListener("pointerdown", function () { | |
| button.style.backgroundColor = "lightblue"; | |
| }); | |
| button.addEventListener("pointerup", function () { | |
| setTimeout(() => { button.style.backgroundColor = originalColor; }, 1000) | |
| }); | |
| button.addEventListener("pointercancel", function () { | |
| setTimeout(() => { button.style.backgroundColor = originalColor; }, 1000) | |
| }); | |
| }, | |
| showToast: function showToast( | |
| text, | |
| x = 0, | |
| y = 0, | |
| w = 200, | |
| h = 0, | |
| duration = 1000, | |
| zIndex = 9999 | |
| ) { | |
| const textArea = document.createElement("textarea"); | |
| textArea.value = text; | |
| textArea.style.width = w + "px"; | |
| textArea.style.height = h === 0 ? "auto" : h + "px"; | |
| textArea.style.borderWidth = "0"; | |
| textArea.style.outline = "none"; | |
| textArea.style.position = "fixed"; | |
| textArea.style.left = x + "px"; | |
| textArea.style.top = y + "px"; | |
| textArea.style.zIndex = zIndex; | |
| textArea.style.backgroundColor="black"; | |
| textArea.style.color="white"; | |
| textArea.disabled = true; | |
| document.body.appendChild(textArea); | |
| setTimeout(() => { | |
| document.body.removeChild(textArea); | |
| }, duration); | |
| }, | |
| copyToClipboard: function copyToClipboard(text) { | |
| const textArea = document.createElement("textarea"); | |
| textArea.value = text; | |
| textArea.style.width = "50%"; | |
| textArea.style.height = "100px"; | |
| textArea.style.borderWidth = "0"; | |
| textArea.style.outline = "none"; | |
| textArea.style.position = "fixed"; | |
| textArea.style.left = "0"; | |
| textArea.style.top = "0"; | |
| textArea.style.zIndex = "9999999"; | |
| textArea.style.backgroundColor="black"; | |
| textArea.style.color="white"; | |
| document.body.appendChild(textArea); | |
| textArea.select(); | |
| document.execCommand("copy"); | |
| textArea.disabled = true; | |
| textArea.value = "copyed to clipboard \n" + textArea.value; | |
| textArea.scrollTo(10000, 100000); | |
| setTimeout(() => { | |
| document.body.removeChild(textArea); | |
| }, 1000); | |
| }, | |
| writeText: function writeText(targetElement, text, prefix = " ", endfix = " ") { | |
| console.log("writeText(): ", targetElement); | |
| document.execCommand("insertText", false, `${prefix}${text}${endfix}`) || utils.copyToClipboard(text); | |
| }, | |
| dragElement: function dragElement(elmnt, movableElmnt = elmnt.parentElement, speed = 1) { | |
| elmnt.style.touchAction = "none"; | |
| let pos1 = 0, | |
| pos2 = 0, | |
| pos3 = 0, | |
| pos4 = 0; | |
| let rmShadeTimeout; | |
| let shadeDiv; | |
| elmnt.addEventListener("pointerdown", (e) => { | |
| dragMouseDown(e); | |
| }); | |
| function dragMouseDown(e) { | |
| e = e || window.event; | |
| e.preventDefault(); | |
| pos3 = e.clientX; | |
| pos4 = e.clientY; | |
| document.body.addEventListener("pointermove", elementDrag); | |
| document.body.addEventListener("pointerup", closeDragElement); | |
| shadeDiv = | |
| document.querySelector("#shadeDivForDragElement") || | |
| document.createElement("div"); | |
| shadeDiv.id = "shadeDivForDragElement"; | |
| shadeDiv.style.width = "300vw"; | |
| shadeDiv.style.height = "300vh"; | |
| shadeDiv.style.position = "fixed"; | |
| shadeDiv.style.top = "0"; | |
| shadeDiv.style.left = "0"; | |
| shadeDiv.style.backgroundColor = "rgb(230,230,230,0.2)"; | |
| shadeDiv.style.zIndex = 100000; | |
| document.body.appendChild(shadeDiv); | |
| rmShadeTimeout = setTimeout(() => { | |
| let shadeDiv = document.querySelector("#shadeDivForDragElement"); | |
| shadeDiv && document.body.removeChild(shadeDiv); | |
| }, 10000); | |
| } | |
| function elementDrag(e) { | |
| e = e || window.event; | |
| e.preventDefault(); | |
| pos1 = pos3 - e.clientX; | |
| pos2 = pos4 - e.clientY; | |
| movableElmnt.style.position = "fixed"; | |
| movableElmnt.style.top = e.clientY - elmnt.clientHeight / 2 + "px"; | |
| movableElmnt.style.left = e.clientX - elmnt.clientWidth / 2 + "px"; | |
| } | |
| function closeDragElement() { | |
| console.log("closeDragElement(): pointerup"); | |
| document.body.removeEventListener("pointermove", elementDrag); | |
| document.body.removeEventListener("pointerup", closeDragElement); | |
| document.body.removeChild( | |
| document.querySelector("#shadeDivForDragElement") | |
| ); | |
| } | |
| }, | |
| renderMarkdown(mdString, targetElement) { | |
| let headerPattern = /^(#{1,6})\s*(.*)$/gm; | |
| const boldPattern = /\*\*(.*?)\*\*/g; | |
| const linkPattern = /\[(.*?)\]\((.*?)\)/g; | |
| const newlinePattern = /(?:\n)/g; | |
| const inlineCodePattern = /```(.*?)```/g; | |
| const codeBlockPattern = /```(\w+)?\n(.*?)```/gs; | |
| let html = mdString; | |
| let parts = html.split("```"); | |
| for (let i = 0; i < parts.length; i++) { | |
| if (i % 2 === 0) { | |
| parts[i] = parts[i].replace(headerPattern, (match, hash, content) => { | |
| const level = hash.length; | |
| return `<h${level}>${content}</h${level}>`; | |
| }); | |
| parts[i] = parts[i].replace(newlinePattern, (match, hash, content) => { | |
| const level = hash.length; | |
| return `<br>`; | |
| }); | |
| } | |
| } | |
| html = parts.join("```"); | |
| html = html.replace(boldPattern, "<strong>$1</strong>"); | |
| html = html.replace(linkPattern, '<a href="$2">$1</a>'); | |
| html = html.replace(codeBlockPattern, (match, language, code) => { | |
| return ` | |
| <div class="code-block"> | |
| <button class="copy-code-btn">Copy</button> | |
| <button class="insert-code-btn">Insert</button> | |
| <pre id='devilentCodePre'><xmp>${code}</xmp></pre> | |
| </div> | |
| `; | |
| }); | |
| html = html.replace(inlineCodePattern, "<code>$1</code>"); | |
| targetElement.innerHTML = html; | |
| const buttons = targetElement.querySelectorAll(".code-block button"); | |
| buttons.forEach((btn) => { | |
| btn.addEventListener("pointerdown", (e) => { | |
| e.preventDefault(); | |
| const code = btn.parentElement.querySelector("pre").innerText; | |
| if (btn.classList.contains("copy-code-btn")) { | |
| utils.copyToClipboard(code); | |
| } else if (btn.classList.contains("insert-code-btn")) { | |
| console.log("insert button down"); | |
| utils.writeText(document.activeElement, code, "", ""); | |
| } | |
| }); | |
| }); | |
| const copyButton = document.createElement("button"); | |
| copyButton.innerText = "Copy"; | |
| copyButton.addEventListener("pointerdown", (e) => { | |
| e.preventDefault(); | |
| e.stopPropagation(); | |
| utils.copyToClipboard(mdString); | |
| }); | |
| const insertButton = document.createElement("button"); | |
| insertButton.innerText = "Insert"; | |
| insertButton.addEventListener("pointerdown", (e) => { | |
| e.preventDefault(); | |
| e.stopPropagation(); | |
| utils.writeText(document.activeElement, mdString, "", ""); | |
| }); | |
| copyButton.classList.add("copy-btn"); | |
| insertButton.classList.add("insert-btn"); | |
| let editButton = document.createElement("button"); | |
| editButton.innerText = "Edit"; | |
| editButton.addEventListener("pointerdown", (e) => { | |
| e.preventDefault(); | |
| if (targetElement.isEditableElement) { | |
| targetElement.setAttribute('contenteditable', 'false'); | |
| } | |
| else { | |
| targetElement.setAttribute('contenteditable', 'true'); | |
| } | |
| }); | |
| editButton.classList.add("copy-btn"); | |
| const closeButton = document.createElement("button"); | |
| closeButton.innerText = "Close"; | |
| closeButton.addEventListener("pointerdown", (e) => { | |
| e.preventDefault(); | |
| targetElement.remove(); | |
| }); | |
| closeButton.classList.add("copy-btn"); | |
| let buttonContainer = document.createElement("div"); | |
| buttonContainer.classList.add("button-container"); | |
| buttonContainer.appendChild(copyButton); | |
| buttonContainer.appendChild(insertButton); | |
| buttonContainer.appendChild(closeButton); | |
| buttonContainer.appendChild(editButton); | |
| const parentElement = targetElement; | |
| buttonContainer.style.width = "100%"; | |
| buttonContainer.style.backgroundColor = parentElement.style.backgroundColor; | |
| buttonContainer.style.color = | |
| "lighten(" + buttonContainer.style.backgroundColor + ", 20%)"; | |
| targetElement.prepend(buttonContainer); | |
| utils.dragElement(buttonContainer, targetElement); | |
| targetElement.classList.add("markdown-container"); | |
| let markdownContainers = | |
| document.getElementsByClassName("markdown-container"); | |
| for (let i = 0; i < markdownContainers.length; i++) { | |
| markdownContainers[i].style.fontFamily = "Arial, sans-serif"; | |
| markdownContainers[i].style.lineHeight = "1.6"; | |
| markdownContainers[i].style.maxWidth = "800px"; | |
| markdownContainers[i].style.margin = "0 auto"; | |
| markdownContainers[i].style.padding = "0px"; | |
| markdownContainers[i].style.backgroundColor = "azure"; | |
| markdownContainers[i].style.overflow = "auto"; | |
| markdownContainers[i].style.boxShadow = "0px 0px 50px rgba(0, 0, 0, 0.4)"; | |
| } | |
| let codeBlocks = document.getElementsByClassName("code-block"); | |
| for (let i = 0; i < codeBlocks.length; i++) { | |
| codeBlocks[i].style.position = "relative"; | |
| } | |
| let insertCodeBtns = document.getElementsByClassName("insert-code-btn"); | |
| let codecopyBtns = document.getElementsByClassName("copy-code-btn"); | |
| for (let i = 0; i < codecopyBtns.length; i++) { | |
| codecopyBtns[i].style.top = "0"; | |
| codecopyBtns[i].style.position = "absolute"; | |
| codecopyBtns[i].style.right = "0"; | |
| codecopyBtns[i].style.margin = "5px"; | |
| codecopyBtns[i].style.padding = "2px 5px"; | |
| codecopyBtns[i].style.fontSize = "12px"; | |
| codecopyBtns[i].style.border = "none"; | |
| codecopyBtns[i].style.borderRadius = "3px"; | |
| codecopyBtns[i].style.backgroundColor = "#007bff"; | |
| codecopyBtns[i].style.color = "white"; | |
| codecopyBtns[i].style.cursor = "pointer"; | |
| } | |
| for (let i = 0; i < insertCodeBtns.length; i++) { | |
| insertCodeBtns[i].style.position = "absolute"; | |
| insertCodeBtns[i].style.top = "0"; | |
| insertCodeBtns[i].style.right = "50px"; | |
| insertCodeBtns[i].style.margin = "5px"; | |
| insertCodeBtns[i].style.padding = "2px 5px"; | |
| insertCodeBtns[i].style.fontSize = "12px"; | |
| insertCodeBtns[i].style.border = "none"; | |
| insertCodeBtns[i].style.borderRadius = "3px"; | |
| insertCodeBtns[i].style.backgroundColor = "#007bff"; | |
| insertCodeBtns[i].style.color = "white"; | |
| insertCodeBtns[i].style.cursor = "pointer"; | |
| } | |
| let copyBtns = document.getElementsByClassName("copy-btn"); | |
| let insertBtns = document.getElementsByClassName("insert-btn"); | |
| for (let i = 0; i < copyBtns.length; i++) { | |
| copyBtns[i].style.margin = "5px"; | |
| copyBtns[i].style.padding = "2px 5px"; | |
| copyBtns[i].style.fontSize = "12px"; | |
| copyBtns[i].style.border = "none"; | |
| copyBtns[i].style.borderRadius = "3px"; | |
| copyBtns[i].style.backgroundColor = "#007bff"; | |
| copyBtns[i].style.color = "white"; | |
| copyBtns[i].style.cursor = "pointer"; | |
| } | |
| for (let i = 0; i < insertBtns.length; i++) { | |
| insertBtns[i].style.margin = "5px"; | |
| insertBtns[i].style.padding = "2px 5px"; | |
| insertBtns[i].style.fontSize = "12px"; | |
| insertBtns[i].style.border = "none"; | |
| insertBtns[i].style.borderRadius = "3px"; | |
| insertBtns[i].style.backgroundColor = "#007bff"; | |
| insertBtns[i].style.color = "white"; | |
| insertBtns[i].style.cursor = "pointer"; | |
| } | |
| let pres = targetElement.getElementsByTagName("pre"); | |
| for (let i = 0; i < pres.length; i++) { | |
| pres[i].style.backgroundColor = "#f7f7f7"; | |
| pres[i].style.borderRadius = "5px"; | |
| pres[i].style.padding = "10px"; | |
| pres[i].style.whiteSpace = "pre-wrap"; | |
| pres[i].style.wordBreak = "break-all"; | |
| } | |
| let codes = targetElement.getElementsByTagName("code"); | |
| for (let i = 0; i < codes.length; i++) { | |
| codes[i].style.backgroundColor = "#f1f1f1"; | |
| codes[i].style.borderRadius = "3px"; | |
| codes[i].style.padding = "2px 5px"; | |
| codes[i].style.fontFamily = "'Courier New', Courier, monospace"; | |
| } | |
| }, | |
| displayMarkdown(mdString) { | |
| let containerID = "ai_input_md_dispalyer"; | |
| let container = document.getElementById(containerID); | |
| if (container === null) { | |
| container = | |
| document.getElementById(containerID) || document.createElement("div"); | |
| container.id = containerID; | |
| document.body.appendChild(container); | |
| container.style.zIndex = "100000"; | |
| container.style.position = "fixed"; | |
| container.style.top = "70vh"; | |
| container.style.left = "0"; | |
| container.style.height = "40vh"; | |
| container.style.width = "80vw"; | |
| container.style.backgroundColor = "rgba{20,20,50,1}"; | |
| } | |
| utils.renderMarkdown(mdString, container); | |
| let div = document.createElement('div'); | |
| div.style.height = '3000px'; | |
| container.appendChild(div) | |
| }, | |
| moveElementNearMouse: (mElem, targetElement, alwayInWindow = true, event) => { | |
| let x = event.clientX + 200; | |
| let y = event.clientY - 20; | |
| console.log('moveElementNearMouse: ', x, y); | |
| if (alwayInWindow) { | |
| x = Math.abs(x); | |
| y = Math.abs(y); | |
| x = Math.min(x, window.innerWidth - mElem.clientWidth); | |
| y = Math.min(y, window.innerHeight - 10 - mElem.clientHeight); | |
| } | |
| mElem.style.left = x + "px"; | |
| mElem.style.top = y + "px"; | |
| }, | |
| addEventListenerForActualClick(element, handler) { | |
| let initialX, initialY; | |
| let startTime; | |
| element.addEventListener("pointerdown", (event) => { | |
| initialX = event.clientX; | |
| initialY = event.clientY; | |
| startTime = Date.now(); | |
| }); | |
| element.addEventListener("pointerup", (event) => { | |
| const deltaX = Math.abs(event.clientX - initialX); | |
| const deltaY = Math.abs(event.clientY - initialY); | |
| if (deltaX <= 10 && deltaY <= 10 && Date.now() - startTime < 1000) { | |
| console.log( | |
| "Minimal mouse movement (< 10px in either direction) and short duration click detected." | |
| ); | |
| handler(event); | |
| } | |
| }); | |
| }, | |
| sendKeyEvent(element, key, modifiers) { | |
| const eventDown = new KeyboardEvent("keydown", { | |
| key: key, | |
| code: key.toUpperCase(), | |
| bubbles: true, | |
| cancelable: true, | |
| ...modifiers, | |
| }); | |
| const eventUp = new KeyboardEvent("keyup", { | |
| key: key, | |
| code: key.toUpperCase(), | |
| bubbles: true, | |
| cancelable: true, | |
| ...modifiers, | |
| }); | |
| element.dispatchEvent(eventDown); | |
| element.dispatchEvent(eventUp); | |
| }, | |
| blobToBase64: function blobToBase64(blob) { | |
| if (!(blob instanceof Blob)) { | |
| throw new TypeError("Parameter must be a Blob object."); | |
| } | |
| if (!blob.size) { | |
| throw new Error("Empty Blob provided."); | |
| } | |
| return new Promise((resolve, reject) => { | |
| const reader = new FileReader(); | |
| reader.onload = () => resolve(reader.result.split(",")[1]); | |
| reader.onerror = reject; | |
| reader.readAsDataURL(blob); | |
| }); | |
| }, | |
| playAudioBlob: function playAudioBlob(blob, autoPlay = true) { | |
| const audio = new Audio(); | |
| audio.src = URL.createObjectURL(blob); | |
| audio.controls = true; | |
| document.body.prepend(audio); | |
| if (autoPlay === true) { | |
| audio | |
| .play() | |
| .then(() => { | |
| console.log("Audio played successfully!"); | |
| }) | |
| .catch((error) => { | |
| console.error("Error playing audio:", error); | |
| }); | |
| } | |
| }, | |
| async AIComplete(userText, option = { | |
| url: '/openai/v1/chat/completions', | |
| model: 'llama3-70b-8192', | |
| max_tokens: 8000, | |
| }) { | |
| console.log("AIcomplete(): ", userText); | |
| if (utils.checkValidString(userText) === false) { | |
| return; | |
| } | |
| let response = await fetch( | |
| option.url, | |
| { | |
| headers: { | |
| accept: "*/*", | |
| "content-type": "application/json", | |
| }, | |
| body: JSON.stringify({ | |
| messages: [{ | |
| "role": "system", | |
| "content": "be concise and clear." | |
| }, | |
| { role: "user", content: userText }], | |
| model: option.model, | |
| tools: [], | |
| temperature: 0.7, | |
| top_p: 0.8, | |
| max_tokens: option.max_tokens || 1000000, | |
| }), | |
| method: "POST", | |
| } | |
| ); | |
| response = await response.json(); | |
| let responseMessage = response?.choices[0]?.message?.content; | |
| console.log("[leptonComplete(text)]", responseMessage); | |
| let mdContainer = document.createElement("div"); | |
| document.body.appendChild(mdContainer); | |
| utils.displayMarkdown(userText + "\n\n" + option.model + '\n' + responseMessage); | |
| return response; | |
| } | |
| }; | |
| // ... (Other external API functions: sendAudioToApi, whisperjaxws, | |
| // sendAudioToCFWhisperApi, sendAudioToHFWhisperApi) ... | |
| export { utils }; |