import { api } from "../../../scripts/api.js"; export function get_position_style(ctx, widget_width, y, node_height) { const MARGIN = 4; const elRect = ctx.canvas.getBoundingClientRect(); const transform = new DOMMatrix() .scaleSelf(elRect.width / ctx.canvas.width, elRect.height / ctx.canvas.height) .multiplySelf(ctx.getTransform()) .translateSelf(MARGIN, MARGIN + y); return { transformOrigin: '0 0', transform: transform, left: `0px`, top: `0px`, position: "absolute", maxWidth: `${widget_width - MARGIN*2}px`, maxHeight: `${node_height - MARGIN*2}px`, width: `auto`, height: `auto`, } } export function formatAndDisplayJSON(text) { console.log("Input text:", text); // Debug: Log the input if (typeof text !== 'string') { console.log("Input is not a string:", typeof text); return "Error: Input is not a string"; } try { // Try to parse as JSON const jsonObject = JSON.parse(text); const formattedJSON = JSON.stringify(jsonObject, null, 2); console.log("Formatted JSON:", formattedJSON); // Debug: Log the result return formattedJSON; } catch (jsonError) { console.log("JSON parse error:", jsonError.message); // Debug: Log the error // Check if it's a Python-like object if (text.trim().startsWith('{') || text.trim().startsWith('[')) { try { const formattedPython = formatPythonLikeObject(text); console.log("Formatted Python-like object:", formattedPython); // Debug: Log the result return formattedPython || text; // Return original text if formatPythonLikeObject returns empty } catch (pythonError) { console.log("Python-like object formatting error:", pythonError.message); // Debug: Log the error } } // If it's not JSON or a Python-like object, return the original text console.log("Returning original text"); return text; } } export function formatPythonLikeObject(text) { let indent = 0; let inString = false; const formatted = []; for (let i = 0; i < text.length; i++) { // ... (rest of the function remains the same) } return formatted.join(''); } export function chainCallback(object, property, callback) { if (object == undefined) { console.error("Tried to add callback to non-existant object") return; } if (property in object) { const callback_orig = object[property] object[property] = function () { const r = callback_orig.apply(this, arguments); callback.apply(this, arguments); return r }; } else { object[property] = callback; } } export async function uploadFile(file) { try { const body = new FormData(); const i = file.webkitRelativePath.lastIndexOf('/'); const subfolder = file.webkitRelativePath.slice(0,i+1) const new_file = new File([file], file.name, { type: file.type, lastModified: file.lastModified, }); body.append("image", new_file); if (i > 0) { body.append("subfolder", subfolder); } const resp = await api.fetchApi("/upload/image", { method: "POST", body, }); if (resp.status === 200) { return resp.status } else { alert(resp.status + " - " + resp.statusText); } } catch (error) { alert(error); } } export function gtUIAddUploadWidget(nodeType, nodeData, widgetName, type="audio") { chainCallback(nodeType.prototype, "onNodeCreated", function() { const pathWidget = this.widgets.find((w) => w.name === widgetName); const fileInput = document.createElement("input"); document.body.append(fileInput); let uploadWidget = this.addWidget("button", "choose " + type + " to upload", "image", () => { //clear the active click event app.canvas.node_widget = null fileInput.click(); }); uploadWidget.options.serialize = false; chainCallback(this, "onRemoved", () => { fileInput?.remove(); }); if (type == "folder") { Object.assign(fileInput, { type: "file", style: "display: none", webkitdirectory: true, onchange: async () => { const directory = fileInput.files[0].webkitRelativePath; const i = directory.lastIndexOf('/'); if (i <= 0) { throw "No directory found"; } const path = directory.slice(0,directory.lastIndexOf('/')) if (pathWidget.options.values.includes(path)) { alert("A folder of the same name already exists"); return; } let successes = 0; for(const file of fileInput.files) { if (await uploadFile(file) == 200) { successes++; } else { //Upload failed, but some prior uploads may have succeeded //Stop future uploads to prevent cascading failures //and only add to list if an upload has succeeded if (successes > 0) { break } else { return; } } } pathWidget.options.values.push(path); pathWidget.value = path; if (pathWidget.callback) { pathWidget.callback(path) } }, }); } else if (type == "text" ) { console.log("text here") Object.assign(fileInput, { type: "file", accept: "text/plain,text/markdown,text/html,text/csv,text/xml,text/yaml,text/json,application/json,application/pdf,application/x-yaml,application/vnd.ms-excel,application/csv,application/tsv,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,text/tab-separated-values,text/info", style: "display: none", onchange: async () => { if (fileInput.files.length) { if (await uploadFile(fileInput.files[0]) != 200) { //upload failed and file can not be added to options return; } const filename = fileInput.files[0].name; pathWidget.options.values.push(filename); pathWidget.value = filename; if (pathWidget.callback) { pathWidget.callback(filename) } } }, }); } else if (type == "video") { Object.assign(fileInput, { type: "file", accept: "video/webm,video/mp4,video/mkv,image/gif", style: "display: none", onchange: async () => { if (fileInput.files.length) { if (await uploadFile(fileInput.files[0]) != 200) { //upload failed and file can not be added to options return; } const filename = fileInput.files[0].name; pathWidget.options.values.push(filename); pathWidget.value = filename; if (pathWidget.callback) { pathWidget.callback(filename) } } }, }); } else if (type == "audio") { Object.assign(fileInput, { type: "file", accept: "audio/mpeg,audio/wav,audio/x-wav,audio/ogg", style: "display: none", onchange: async () => { if (fileInput.files.length) { if (await uploadFile(fileInput.files[0]) != 200) { //upload failed and file can not be added to options return; } const filename = fileInput.files[0].name; const filetype = fileInput.files[0].type; pathWidget.options.values.push(filename); pathWidget.value = filename; if (pathWidget.callback) { pathWidget.callback(filename) } // Create a URL for the audio file and set it as the source of the audio element // const audioURL = URL.createObjectURL(filename); // audioWidget.src = audioURL; } }, }); this.onDragOver = function( e) { if (e.dataTransfer && e.dataTransfer.items) { const audio = [...e.dataTransfer.items].find((f) => f.kind === "file" && f.type.startsWith("audio/")); // console.log("dragover: " + audio); // console.log(audio); return !!audio; } return false; } this.onDragDrop = function (e) { let handled = false; for (const file of e.dataTransfer.files) { if (file.type.startsWith("audio/")) { handled = true; uploadFile(file, !handled); handled = true; const filename = file.name; pathWidget.options.values.push(filename); pathWidget.value = filename; if (pathWidget.callback) { pathWidget.callback(filename) } } } return handled; } }else { throw "Unknown upload type" } }); } export async function getLMStudioModels(baseUrl, port) { const url = `${baseUrl}:${port}/v1/models`; try { const response = await fetch(url); if (!response.ok) { throw new Error(`Failed to fetch models: ${response.status}`); } const modelsInfo = await response.json(); // Extract the model names const models = modelsInfo.data.map(model => model.id); return models; } catch (error) { console.error("Error fetching LM Studio models:", error); return []; } } export async function getOllamaModels(baseUrl, port) { const url = `${baseUrl}:${port}/api/tags`; try { const response = await fetch(url); if (!response.ok) { throw new Error(`Failed to fetch models: ${response.status}`); } const modelsInfo = await response.json(); // Extract the model names const models = modelsInfo.models.map(model => model.name); return models; } catch (error) { console.error("Error fetching Ollama models:", error); return []; } } export async function updatePromptModelList(node, models) { const modelWidget = node.widgets.find((w) => w.name === "model" || w.name === "prompt_model"); if (modelWidget){ const selectedItem = modelWidget.value; modelWidget.options.values = models; // if (models.length > 0) { // modelWidget.value = models[0]; // } const warning = "No models returned from server" if (models.length == 0){ modelWidget.value = warning } if ((selectedItem === "" || selectedItem === null || selectedItem == warning) && models.length > 0 && !models.includes(selectedItem)) { modelWidget.value = models[0]; } } }