class WindowHandler{ /* Eventos generados - chat:enviar - chat:eliminar **********************/ /* Eventos escuchados **********************/ conversacion = [] constructor(conversacion, index, chatHndl){ // El indice de este chat this.index = index this.conversacion = conversacion this.chatHandler = chatHndl || 0 // Template de mensajes this.templateMsg = $("#template-message").contents("div.message").clone() this.tabs = $(".tabs") this.templateLabel = $("#template-label").contents("label").clone() this.templateTab = $("#template-tab").contents("div.tab").clone() this.chatbox = this.templateTab.find(".chat") // Parametros base this.active = false; // ha tenido su primera interacción this.interacted = false this.crearVentanaChat(index, "Tab "+ index) this.cargarEventos(); $(document).trigger("ventana:cambiada") this.cargarChat(conversacion) } crearVentanaChat(index, nombre){ // coloca el valor al input y lo selecciona let newLabel = this.templateLabel.clone() newLabel.find("input").val(index).prop("checked", true); newLabel.find("> div").text(nombre) this.tabs.append(newLabel) this.label = newLabel // Crea la ventana de chat let tempTab = this.templateTab.clone(); tempTab.attr("id", index) $(".chats").append(tempTab) // establece los contextos de ventana this.ctx = tempTab this.chatbox = this.ctx.find(".chat"); this.ventanaSeleccionada() } ventanaSeleccionada(){ // cuando la ventana es seleccionada, cambia $(".tab").removeClass("active") this.label.find("input")[0].checked=true; this.ctx.addClass("active"); } cargarEventos(){ this.label.click(()=> this.ventanaSeleccionada()) this.ctx.find(".input-text").keypress((event) => { if (!event.shiftKey && event.keyCode === 13) { this.manejadorEnviar(); } this.recalcularTextarea() }); this.ctx.find(".input-send").click(() => this.manejadorEnviar()); this.ctx.find(".input-text").on("keyup,keydown", () => this.recalcularTextarea()); this.ctx.find(".input-delete").click(() => this.eliminarChat() ) this.ctx.find(".input-menu").click(() => $(document).trigger("mostrar:opciones")) } crearMensaje(texto, user){ this.active = this.templateMsg.clone(); switch(user){ case "system": return; case "user": this.active.addClass("me"); this.active.find("div p").text(texto); break; case "assistant": texto = this.procesarTexto(texto); this.active.find("div p").html(texto); Prism.highlightAllUnder(this.active[0]) break; case "loading": this.active.find("div p").html('
'); break; } this.chatbox.append(this.active); this.chatbox.scrollTop(this.chatbox[0].scrollHeight); this.interacted=true return this.active } manejadorEnviar(){ let mensaje = this.ctx.find(".input-text").val(); this.ctx.find("button").prop("disabled", true); this.ctx.find("textarea").prop("disabled", true); if(mensaje==""){ return false; } if(this.conversacion.length==0){ this.conversacion.push({role:"system", content: this.chatHandler.config.assistantPrompt}) } this.conversacion.push({role: "user", content: mensaje}) self = this this.crearMensaje(mensaje, "user"); $(document).trigger("chat:enviar", {ctx: self, conversacion:this.conversacion}) } respuestaInicio(){ this.crearMensaje("", "loading"); } respuestaStatus(mensaje, modo=false){ let temp = $("
") temp.text(mensaje) if(!this.active.find(".loader").hasClass("firststage")){ this.active.find(".loader").addClass("firststage") } switch(modo){ case "enlinea": this.active.find("div p div:last-child").text(this.active.find("div p div:last-child").text() + mensaje); break; case "reemplazar": this.active.find("div p div:not(.loader-wrap)").remove(); this.active.find("div p").append(temp) break; default: this.active.find("div p").append(temp) } this.chatbox.scrollTop(this.chatbox[0].scrollHeight); } respuestaMensaje(data){ let mensaje = data.content this.active.find("div p").html(""); mensaje = this.procesarTexto(mensaje); this.active.find("div p").html(mensaje); Prism.highlightAllUnder(this.active[0]); this.chatbox.scrollTop(this.chatbox[0].scrollHeight); this.conversacion.push(data) this.active = false; this.interacted = true; this.ctx.find("button").prop("disabled", false); this.ctx.find("textarea").prop("disabled", false); this.ctx.find("textarea").val("") this.ctx.find("textarea").focus(); $(document).trigger("chat:salvar", {index: this.index, conversacion: this.conversacion}) } respuestaError(error){ this.ctx.find("button").prop("disabled", false); this.ctx.find("textarea").prop("disabled", false); this.ctx.find("textarea").val("") this.ctx.find("textarea").focus(); if(error.hasOwnProperty("responseJSON")){ this.active.find("div p").html(error.responseJSON.detail) }else{ this.active.find("div p").html("El API no responde, la conexión pudo haberse caido") } switch(error.status | 0){ case 404: this.active.addClass("error") break; case 408: this.active.addClass("warning") break; default: this.active.addClass("error") } this.active = false; this.chatbox.scrollTop(this.chatbox[0].scrollHeight) } cargarChat(conversacion){ for(let mensaje of this.conversacion){ this.crearMensaje(mensaje.content, mensaje.role) } } eliminarChat(){ if(confirm("¿Estás seguro que quieres eliminar esta conversación?")){ this.label.remove() this.ctx.remove() $(document).trigger("chat:eliminar", {ctx:this.ctx, index:this.index}) } } recalcularTextarea(){ this.ctx.find(".input-box").css("height", "30px"); let height = parseInt((this.ctx.find(".input-text").prop('scrollHeight')+15)/15)*15; this.ctx.find(".input-box").css("height", height+"px"); height -= 30; this.ctx.find(".chat").css("--textarea", height+"px"); } procesarTexto(texto){ let resultado = ""; let codigo = false; for(let actual of texto.split("```")){ if(codigo){ let temp = actual.split("\n",1); resultado += "
"+temp+"
"; }else{ resultado += $("
").text(actual).html().replace(/`([^`]+?)`/gm, "$1").replace(/\n/g, "
"); resultado = resultado.replace(/\[(.*?)\]\((.*?)\)/gm, "[$1]") } codigo = !codigo; } return resultado } }