const { observeDom, ui: { injectCss, Button, openModal, ModalRoot, ModalHeader, ModalBody, ModalFooter, ModalSizes, Text, TextBox, ReactiveRoot, TextArea, }, plugin: { store }, util: { getFiber }, } = shelter; let popupButton = null; let unobserve = null; const getMessageHistory = () => { const messageElements = document.querySelectorAll('div[class^="message-"]'); const messages = [...messageElements].map((message) => ({ username: message.querySelector("h3 > span > span")?.textContent, message: message.querySelector("div > div > div").textContent, })); return messages.reduce((acc, message) => { if (message.username) { acc.push(message); } else { acc[acc.length - 1].message += `\n${message.message}`; } return acc; }, []); }; const loadingIndicator = () => ( ); // Credits to yellowsink for this messagebar stuff // https://github.com/yellowsink const appendTextToMessagebar = (text) => { const elem = document.querySelector('[class*="slateContainer"]'); const fiber = getFiber(elem); const editor = fiber.child.pendingProps.editor; editor.insertText(text); }; export function onLoad() { injectCss(` .label-spacing { margin-bottom: .125rem; } .mb-2 { margin-bottom: .5rem; } .pr-2 { padding-right: .5rem; }`); const openGenerationModal = async () => { let model = store.model || ""; let prompt = ""; closeModal = openModal(() => ( closeModal()}>Generate Response Model { model = e; }} /> Prompt { prompt = e; }} /> { closeModal(); const usernameEl = document.querySelector( "[class^=nameTag] > div", ); const myUsername = usernameEl?.textContent ?? "unknown"; store.model = model; const messages = [ ...getMessageHistory() .slice(-7) .map((message) => ({ role: "user", content: `${message.username}: ${message.message}`, })), { role: "system", content: `generate a response as "${myUsername}" according to the prompt: "${prompt}"`, }, ]; // add loading indicator const messageBar = document.querySelector( '[class*="slateContainer"]', ); const { x, y } = messageBar.getBoundingClientRect(); const loadingIndicatorElem = document.body.appendChild(loadingIndicator()); loadingIndicatorElem.style.position = "absolute"; loadingIndicatorElem.style.left = `${x}px`; loadingIndicatorElem.style.top = `${y + 12}px`; // Unsloth Studio API const apiUrl = `${store.unslothUrl}/v1/chat/completions`; const apiHeaders = { "Content-Type": "application/json", }; if (store.unslothKey) { apiHeaders["Authorization"] = `Bearer ${store.unslothKey}`; } const apiBody = { model, messages, stream: false, }; try { const res = await fetch(apiUrl, { method: "POST", headers: apiHeaders, body: JSON.stringify(apiBody), }); const body = await res.json(); if (!res.ok) { throw new Error( `${res.status} ${res.statusText}${body.detail ? `: ${body.detail}` : ""}`, ); } const response = body.choices[0].message.content; appendTextToMessagebar(response.trim()); } catch (err) { console.error("API Error:", err); alert(`Error: ${err.message || "Failed to generate response"}`); } finally { loadingIndicatorElem.remove(); } }} > Generate )); }; unobserve = observeDom( '[class^="channelTextArea"] [class^="buttons"]', (node) => { if (document.querySelector("#generate-button")) return; const secondLastChild = node.lastChild.previousSibling; popupButton = node.insertBefore( , node.firstChild, ); }, ); } export function onUnload() { unobserve(); popupButton?.remove(); } export { default as settings } from "./settings";