AnP/Public/ecma/Components/AIChatComponent.ecma.js
2026-06-13 09:50:59 +02:00

252 lines
8.7 KiB
JavaScript

"use strict";
import {
Fieldset, Section, Form, Div, Nav, UL, LI, Span, Pre, Article
} from "../Utils/HTMLDSL.ecma.js";
import {MarkDown} from "../Utils/MarkDown.ecma.js";
import {Common} from "../Utils/Common.ecma.js";
/**
* @typedef {import("../Application/AnP.ecma.js").AnP} AnP
*/
/**
* @class AIChatComponent
* @constructor
* @param {!AnP} anp
* @returns {void}
* @access private
* @static
*/
export const AIChatComponent = (function(){
/**
* @constructs AIChatComponent
* @param {!AnP} anp
* @returns {void}
* @access private
* @static
*/
const AIChatComponent = function(anp){
/** @type {AIChatComponent} */
const self = this;
/** @type {string|null} */
let web_socket_id = null;
/**
* @returns {void}
* @access private
*/
const constructor = () => {};
this.build = (inputs = null) => {
const name = Common.get_value("name", inputs, "aichat");
return Fieldset({class : "aichat"}, [
anp.components.i18n(name, "legend"),
Section({class : "conversations"}, [
Article({
class : "messages",
data_conversation : anp.unique_keys.get(),
data_selected : true
})
]),
Form({
method : "post",
action : "#",
on_submit : send,
on_key_down : check_keys
}, [
anp.components.text("message", {
multiline : true
}),
anp.components.button("send", "submit")
])
]);
};
const build_data_icon = (name, value) => LI({
data_name : name,
data_i18n : value,
data_i18n_without : true,
title : anp.i18n.get(value)
}, [
anp.components.icon(value),
anp.components.i18n(value)
]);
const build_data_item = (name, value = null) => LI({
data_name : name,
data_i18n : name,
data_i18n_without : true,
title : anp.i18n.get(name)
}, [
anp.components.icon(name),
anp.components.i18n(name),
value === null ? null : Span({class : "value"}, "" + value)
]);
const get_message_box = item => {
while((!item.classList || !item.classList.contains("message")) && (item = item.parentElement));
return item;
};
const set_view = (item, event) => {
const box = get_message_box(item),
mode = item.getAttribute("data-i18n");
box.getAttribute("data-mode") != mode && box.setAttribute("data-mode", mode);
};
const format = content => {
const html = MarkDown.to_html(content);
return [html, html.replace(/</g, "&lt;").replace(/>/g, "&gt;"), content];
};
const build_message = (id, type, content = "", mode = "waiting") => {
const [html, raw_html, md] = format(content),
date = Date.now();
return Fieldset({
class : "message",
data_id : id,
data_type : type,
data_ok : true,
data_status : mode,
data_done : mode == "done",
data_mode : "html"
}, [
anp.components.i18n(type, "legend"),
Div({class : "message-box html-content"}, html),
Pre({class : "message-box raw-html-content"}, raw_html),
Pre({class : "message-box md-content"}, md),
Nav({class : "view buttons"}, [
anp.components.button("html", set_view),
anp.components.button("raw_html", set_view),
anp.components.button("md", set_view),
]),
UL({class : "data"}, [
build_data_icon("status", mode),
build_data_item("ok"),
build_data_item("done"),
build_data_item("date_from", date),
build_data_item("date_to", date),
build_data_item("time", 0),
build_data_item("length", content.length),
build_data_item("response_tokens", content.replace(/(?:[^a-z0-9]+|[\r\n]+)+/gi, " ").trim().split(" ").length)
])
]);
};
const send = (item, event) => {
const text_box = item.querySelector("[name=message]"),
text = text_box.value.trim(),
conversation = document.querySelector(".aichat [data-conversation][data-selected=true]").getAttribute("data-conversation");
event.preventDefault();
if(text){
const data_id = anp.unique_keys.get();
Common.HTML(
".aichat [data-conversation=" + conversation + "]",
build_message(data_id, "user", text, "done"),
build_message(data_id, "bot")
);
text_box.value = "";
anp.web_sockets_clients.send("anp", "ai", "message", {
message_id : data_id,
message : text,
conversation : conversation
});
};
return false;
};
const check_keys = (item, event) => {
event.key == "Enter" && !event.shiftKey && send(item, event);
};
const set_status = (conversation, message, status) => {
// console.log([conversation, message, status]);
const box = document.querySelector(".aichat [data-conversation='" + conversation + "']>[data-type=bot][data-id='" + message + "']"),
status_box = box.querySelector("[data-name=status]"),
status_i18n_box = status_box.querySelector("[data-i18n]"),
status_text = anp.i18n.get(status);
box.setAttribute("data-status", status);
status_box.setAttribute("data-i18n", status);
status_box.setAttribute("title", status_text);
status_box.querySelector("[data-icon]").setAttribute("data-icon", status);
status_i18n_box.setAttribute("data-i18n", status_text);
status_i18n_box.innerHTML = status_text;
};
this.write_response = (conversation, message, fragment, ok, done) => {
// console.log([id, fragment]);
const box = document.querySelector(".aichat .messages>[data-type=bot][data-id='" + message + "']"),
// status = (
// !ok ? "error" :
// done ? "done" :
// "loading"),
// status_text = anp.i18n.get(status),
// status_box = box.querySelector("[data-name=status]"),
// status_i18n_box = status_box.querySelector("[data-i18n]"),
date = Date.now(),
tokens_box = box.querySelector("[data-name=response_tokens] .value"),
[html, raw_html, md] = format(box.querySelector(".md-content").textContent += fragment);
box.querySelector(".html-content").innerHTML = html;
box.querySelector(".raw-html-content").innerHTML = raw_html;
box.querySelector("[data-name=length] .value").innerText = md.length;
box.setAttribute("data-ok", ok);
box.setAttribute("data-done", done);
// box.setAttribute("data-status", status);
// status_box.setAttribute("data-i18n", status);
// status_box.setAttribute("title", status_text);
// status_box.querySelector("[data-icon]").setAttribute("data-icon", status);
// status_i18n_box.setAttribute("data-i18n", status_text);
// status_i18n_box.innerHTML = status_text;
set_status(conversation, message, (
!ok ? "error" :
done ? "done" :
"loading"));
box.querySelector("[data-name=date_to]").querySelector(".value").innerHTML = date;
box.querySelector("[data-name=time]").querySelector(".value").innerHTML = date - Number(box.querySelector("[data-name=date_from] .value").innerText);
tokens_box.innerText = Number(tokens_box.innerText) + 1;
};
this.change_status = (conversation, message, status) => {
set_status(conversation, message, status);
};
constructor();
};
return AIChatComponent;
})();