#wip: Exporter projects.

This commit is contained in:
mbruzon 2026-06-20 10:04:54 +02:00
parent fef6627c83
commit f642ad8b22
38 changed files with 722 additions and 80 deletions

View File

@ -210,8 +210,8 @@
"AnP_HTTPServersManager_start" : null, "AnP_HTTPServersManager_start" : null,
"default_http_servers" : { "default_http_servers" : {
"anp" : { "anp" : {
"type2" : "HTTPServerDriver", "type" : "HTTPServerDriver",
"type" : "HTTPSocketServerDriver", "type2" : "HTTPSocketServerDriver",
"host" : "", "host" : "",
"port" : 18000 "port" : 18000
} }

View File

@ -83,6 +83,11 @@ export const AnP = (function(){
/** @type {WebSocketsClientsManager} */ /** @type {WebSocketsClientsManager} */
this.web_sockets_clients = new WebSocketsClientsManager(self); this.web_sockets_clients = new WebSocketsClientsManager(self);
/** @type {HTMLElement|Document} */
this.item_self = document;
/** @type {string|null} */
this.hash_self = null;
/** /**
* @returns {void} * @returns {void}
* @access private * @access private
@ -139,7 +144,10 @@ export const AnP = (function(){
if(ok !== false && self.settings.get("build_base_gui")){ if(ok !== false && self.settings.get("build_base_gui")){
Common.preload(self.settings.get("position"), (position, asynchronous, ok) => { Common.preload(self.settings.get("position"), (position, asynchronous, ok) => {
if(position){ if(position){
Common.HTML(position, self.components.base.build()); self.hash_self = (
self.item_self = Common.HTML(position, self.components.base.build())[0]
).getAttribute("data-hash");
self.routes.refresh();
end(true); end(true);
}else }else
end(false); end(false);

View File

@ -12,7 +12,7 @@ import { AIChatComponent } from "../Components/AIChatComponent.ecma.js";
import {Common} from "../Utils/Common.ecma.js"; import {Common} from "../Utils/Common.ecma.js";
import {Check} from "../Utils/Checks.ecma.js"; import {Check} from "../Utils/Checks.ecma.js";
import { import {
Span, Img, Button, Label, Input, Textarea, Div Span, Img, Button, Label, Input, Textarea, Div, Fieldset
} from "../Utils/HTMLDSL.ecma.js"; } from "../Utils/HTMLDSL.ecma.js";
/** /**
@ -67,7 +67,7 @@ export const Components = (function(){
/** @type {I18NComponent} */ /** @type {I18NComponent} */
this.i18n_selector = new I18NComponent(anp); this.i18n_selector = new I18NComponent(anp);
/** @type {AIChatComponent} */ /** @type {AIChatComponent} */
this.aichat = new AIChatComponent(anp); this.aichat_manager = new AIChatComponent(anp);
/** /**
* @returns {void} * @returns {void}
@ -88,7 +88,7 @@ export const Components = (function(){
* @access private * @access private
*/ */
const thread_method = () => { const thread_method = () => {
document.querySelectorAll(".anp .input .field-information[data-i18n=length]").forEach(item => { anp.item_self.querySelectorAll(".input .field-information[data-i18n=length]").forEach(item => {
/** @type {HTMLSpanElement} */ /** @type {HTMLSpanElement} */
const field = item.querySelector(".value"), const field = item.querySelector(".value"),
@ -473,6 +473,16 @@ export const Components = (function(){
self.button(name, action, inputs) self.button(name, action, inputs)
))); )));
/**
* @param {!number} code
* @returns {Array.<any|null>}
* @access public
*/
this.http_error = code => Fieldset({class : "error-" + code}, [
self.i18n("http_error", "legend", {code : code}),
self.i18n("http_error_" + code, "p")
]);
constructor(); constructor();
}; };

View File

@ -30,21 +30,46 @@ export const AIChatComponent = (function(){
const AIChatComponent = function(anp){ const AIChatComponent = function(anp){
/** @type {AIChatComponent} */ /** @type {AIChatComponent} */
const self = this; const self = this,
/** @type {string|null} */ /** @type {Object.<string, string|number|Object.<string, any|null>>} */
let web_socket_id = null; cache = {};
/** /**
* @returns {void} * @returns {void}
* @access private * @access private
*/ */
const constructor = () => {}; const constructor = () => {
setTimeout(() => {
anp.components.aichat = self.build;
}, 0);
};
/**
* @param {!HTMLElement} item
* @returns {HTMLFielsetElement}
* @access private
*/
const get_chat_box = item => {
while((!item.classList || !item.classList.contains("aichat")) && (item = item.parentElement));
return item;
};
/**
* @param {?(Object.<string, any|null>|Array.<any|null>)} [inputs = null]
* @returns {Array.<any|null>}
* @access public
*/
this.build = (inputs = null) => { this.build = (inputs = null) => {
/** @type {string} */
const name = Common.get_value("name", inputs, "aichat"); const name = Common.get_value("name", inputs, "aichat");
return Fieldset({class : "aichat"}, [ return Fieldset({
class : "aichat",
data_name : Common.get_value("name", inputs, "aichat")
}, [
anp.components.i18n(name, "legend"), anp.components.i18n(name, "legend"),
Section({class : "conversations"}, [ Section({class : "conversations"}, [
Article({ Article({
@ -57,7 +82,12 @@ export const AIChatComponent = (function(){
method : "post", method : "post",
action : "#", action : "#",
on_submit : send, on_submit : send,
on_key_down : check_keys on_key_down : check_keys,
data_connection : Common.data_encode([
Common.get_value("web_socket", inputs, "anp"),
Common.get_value("controller", inputs, "ai"),
Common.get_value(["action", "method"], inputs, "message")
])
}, [ }, [
anp.components.text("message", { anp.components.text("message", {
multiline : true multiline : true
@ -67,6 +97,12 @@ export const AIChatComponent = (function(){
]); ]);
}; };
/**
* @param {!string} name
* @param {!string} value
* @returns {Array.<any|null>}
* @access private
*/
const build_data_icon = (name, value) => LI({ const build_data_icon = (name, value) => LI({
data_name : name, data_name : name,
data_i18n : value, data_i18n : value,
@ -77,6 +113,12 @@ export const AIChatComponent = (function(){
anp.components.i18n(value) anp.components.i18n(value)
]); ]);
/**
* @param {!string} name
* @param {?string} [value = null]
* @returns {Array.<any|null>}
* @access private
*/
const build_data_item = (name, value = null) => LI({ const build_data_item = (name, value = null) => LI({
data_name : name, data_name : name,
data_i18n : name, data_i18n : name,
@ -88,6 +130,11 @@ export const AIChatComponent = (function(){
value === null ? null : Span({class : "value"}, "" + value) value === null ? null : Span({class : "value"}, "" + value)
]); ]);
/**
* @param {!HTMLElement} item
* @returns {HTMLTextAreaElement}
* @access private
*/
const get_message_box = item => { const get_message_box = item => {
while((!item.classList || !item.classList.contains("message")) && (item = item.parentElement)); while((!item.classList || !item.classList.contains("message")) && (item = item.parentElement));
@ -95,25 +142,49 @@ export const AIChatComponent = (function(){
return item; return item;
}; };
/**
* @param {!HTMLElement} item
* @param {!Event} event
* @returns {void}
* @access private
*/
const set_view = (item, event) => { const set_view = (item, event) => {
/** @type {HTMLTextAreaElement} */
const box = get_message_box(item), const box = get_message_box(item),
/** @type {string} */
mode = item.getAttribute("data-i18n"); mode = item.getAttribute("data-i18n");
box.getAttribute("data-mode") != mode && box.setAttribute("data-mode", mode); box.getAttribute("data-mode") != mode && box.setAttribute("data-mode", mode);
}; };
/**
* @param {!string} content
* @returns {[string, string, string]}
* @access private
*/
const format = content => { const format = content => {
/** @type {string} */
const html = MarkDown.to_html(content); const html = MarkDown.to_html(content);
return [html, html.replace(/</g, "&lt;").replace(/>/g, "&gt;"), content]; return [html, html.replace(/</g, "&lt;").replace(/>/g, "&gt;"), content];
}; };
/**
* @param {!string} id
* @param {!string} type
* @param {?string} [content = ""]
* @param {?string} [mode = "waiting"]
* @returns {HTMLFielsetElement}
* @access private
*/
const build_message = (id, type, content = "", mode = "waiting") => { const build_message = (id, type, content = "", mode = "waiting") => {
/** @type {[string, string, string]} */
const [html, raw_html, md] = format(content), const [html, raw_html, md] = format(content),
/** @type {number} */
date = Date.now(); date = Date.now();
return Fieldset({ return Fieldset({
@ -147,17 +218,32 @@ export const AIChatComponent = (function(){
]); ]);
}; };
/**
*
* @param {!HTMLElement} item
* @param {!Event} event
* @returns {boolean}
* @access private
*/
const send = (item, event) => { const send = (item, event) => {
/** @type {HTMLTextAreaElement} */
const text_box = item.querySelector("[name=message]"), const text_box = item.querySelector("[name=message]"),
/** @type {string} */
text = text_box.value.trim(), text = text_box.value.trim(),
conversation = document.querySelector(".aichat [data-conversation][data-selected=true]").getAttribute("data-conversation"); /** @type {HTMLArticleElement} */
conversation = anp.item_self.querySelector(".aichat [data-conversation][data-selected=true]").getAttribute("data-conversation");
event.preventDefault(); event.preventDefault();
if(text){ if(text){
const data_id = anp.unique_keys.get(); console.log([item, item.querySelector("[name=message]"), item.querySelector("[name=message]").parentNode.parentNode.getAttribute("data-connection")]);
/** @type {string} */
const data_id = anp.unique_keys.get(),
/** @type {[string, string, string]} */
[web_socket, controller, method] = Common.data_decode(item.querySelector("[name=message]").parentNode.parentNode.getAttribute("data-connection"));
Common.HTML( Common.HTML(
".aichat [data-conversation=" + conversation + "]", ".aichat [data-conversation=" + conversation + "]",
@ -166,7 +252,7 @@ export const AIChatComponent = (function(){
); );
text_box.value = ""; text_box.value = "";
anp.web_sockets_clients.send("anp", "ai", "message", { anp.web_sockets_clients.send(web_socket, controller, method, {
message_id : data_id, message_id : data_id,
message : text, message : text,
conversation : conversation conversation : conversation
@ -177,16 +263,32 @@ export const AIChatComponent = (function(){
return false; return false;
}; };
/**
* @param {!HTMLElement} item
* @param {!Event} event
* @returns {void}
* @access private
*/
const check_keys = (item, event) => { const check_keys = (item, event) => {
event.key == "Enter" && !event.shiftKey && send(item, event); event.key == "Enter" && !event.shiftKey && send(item, event);
}; };
/**
* @param {!string} conversation
* @param {!string} message
* @param {!string} status
* @returns {void}
* @access private
*/
const set_status = (conversation, message, status) => { 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 + "']"), /** @type {HTMLFieldSetElement} */
const box = anp.item_self.querySelector(".aichat [data-conversation='" + conversation + "']>[data-type=bot][data-id='" + message + "']"),
/** @type {HTMLSpanElement} */
status_box = box.querySelector("[data-name=status]"), status_box = box.querySelector("[data-name=status]"),
/** @type {HTMLSpanElement} */
status_i18n_box = status_box.querySelector("[data-i18n]"), status_i18n_box = status_box.querySelector("[data-i18n]"),
/** @type {string} */
status_text = anp.i18n.get(status); status_text = anp.i18n.get(status);
@ -200,19 +302,24 @@ export const AIChatComponent = (function(){
}; };
/**
* @param {!string} conversation
* @param {!string} message
* @param {!string} fragment
* @param {!boolean} ok
* @param {!boolean} done
* @returns {void}
* @access public
*/
this.write_response = (conversation, message, fragment, ok, done) => { this.write_response = (conversation, message, fragment, ok, done) => {
// console.log([id, fragment]);
const box = document.querySelector(".aichat .messages>[data-type=bot][data-id='" + message + "']"), /** @type {HTMLFieldSetElement} */
// status = ( const box = anp.item_self.querySelector(".aichat .messages>[data-type=bot][data-id='" + message + "']"),
// !ok ? "error" : /** @type {number} */
// 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(), date = Date.now(),
/** @type {HTMLSpanElement} */
tokens_box = box.querySelector("[data-name=response_tokens] .value"), tokens_box = box.querySelector("[data-name=response_tokens] .value"),
/** @type {[string, string, string]} */
[html, raw_html, md] = format(box.querySelector(".md-content").textContent += fragment); [html, raw_html, md] = format(box.querySelector(".md-content").textContent += fragment);
box.querySelector(".html-content").innerHTML = html; box.querySelector(".html-content").innerHTML = html;
@ -222,13 +329,6 @@ export const AIChatComponent = (function(){
box.setAttribute("data-ok", ok); box.setAttribute("data-ok", ok);
box.setAttribute("data-done", done); 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, ( set_status(conversation, message, (
!ok ? "error" : !ok ? "error" :
@ -241,6 +341,13 @@ export const AIChatComponent = (function(){
}; };
/**
* @param {!string} conversation
* @param {!string} message
* @param {!string} status
* @returns {void}
* @access public
*/
this.change_status = (conversation, message, status) => { this.change_status = (conversation, message, status) => {
set_status(conversation, message, status); set_status(conversation, message, status);
}; };

View File

@ -170,7 +170,8 @@ export const BaseComponent = (function(){
Nav({class : "top-menu"}, [ Nav({class : "top-menu"}, [
UL(null, anp.settings.get("main_menu", inputs, [ UL(null, anp.settings.get("main_menu", inputs, [
["home", "#"], ["home", "#"],
["git", git, "_blank"] ["git", git, "_blank"],
["ai", "#/ai"]
]).map(([name, link, target]) => LI({ ]).map(([name, link, target]) => LI({
data_i18n : name, data_i18n : name,
data_i18n_without : true, data_i18n_without : true,
@ -188,7 +189,7 @@ export const BaseComponent = (function(){
anp.components.session_mini.build() anp.components.session_mini.build()
]), ]),
Main(null, [ Main(null, [
anp.components.aichat.build(inputs) // anp.components.aichat.build(inputs)
]), ]),
Footer(null, [ Footer(null, [
anp.components.buttons([ anp.components.buttons([

View File

@ -39,7 +39,7 @@ export const AIController = (function(){
* @access private * @access private
*/ */
this.message = (data, code) => { this.message = (data, code) => {
anp.components.aichat.write_response( anp.components.aichat_manager.write_response(
data.data.conversation, data.data.conversation,
data.data.message, data.data.message,
data.data.response, data.data.response,
@ -55,7 +55,7 @@ export const AIController = (function(){
* @access private * @access private
*/ */
this.status = (data, code) => { this.status = (data, code) => {
anp.components.aichat.change_status( anp.components.aichat_manager.change_status(
data.data.conversation, data.data.conversation,
data.data.message, data.data.message,
data.data.status data.data.status

View File

@ -2,6 +2,8 @@
import {Common} from "../Utils/Common.ecma.js"; import {Common} from "../Utils/Common.ecma.js";
import {RouteModel} from "../Models/RouteModel.ecma.js"; import {RouteModel} from "../Models/RouteModel.ecma.js";
import {Check} from "../Utils/Checks.ecma.js";
import {RE} from "../Utils/Patterns.ecma.js";
/** /**
* @typedef {import("../AnP.ecma.js").AnP} AnP * @typedef {import("../AnP.ecma.js").AnP} AnP
@ -23,6 +25,11 @@ export const RoutesManager = (function(){
* @return {boolean} * @return {boolean}
*/ */
/**
* @callback default_callback
* @return {void}
*/
/** /**
* @constructs RoutesManager * @constructs RoutesManager
* @param {!AnP} anp * @param {!AnP} anp
@ -67,11 +74,19 @@ export const RoutesManager = (function(){
if(last_url != current_url){ if(last_url != current_url){
last_url = current_url; last_url = current_url;
self.go(last_url); self.refresh();
}; };
}; };
/**
* @returns {void}
* @access public
*/
this.refresh = () => {
self.go(last_url);
};
/** /**
* @param {?continue_callback} callback * @param {?continue_callback} callback
* @returns {boolean} * @returns {boolean}
@ -138,11 +153,114 @@ export const RoutesManager = (function(){
return true; return true;
}; };
this.add = (inputs, overwrite = false, callback = null) => { /**
Common.execute(callback, true); * @param {!(string|RegExp)} route_path
* @param {!string} view
* @param {!boolean} overwrite
* @returns {boolean}
* @access private
*/
const set_new = (route_path, view, permissions, overwrite) => {
/** @type {RouteModel} */
const route = new RouteModel(route_path, view, permissions);
let i = null;
if(!routes.some((exists, j) => {
if(exists.route.source == route.route.source){
i = j;
return true;
};
return false;
})){
if(i === null)
routes.push(route);
else if(overwrite)
routes[i] = route;
return true;
};
return false;
}; };
this.go = path => {}; /**
* @param {?any} inputs
* @param {!boolean} [overwrite = false]
* @param {?default_callback} [callback = null]
* @returns {void}
* @access public
*/
this.add = (inputs, overwrite = false, callback = null) => {
if(Check.is_string(inputs)){
/** @type {RegExpMatchArray} */
const matches = (inputs = inputs.trim()).match(RE.ROUTES);
if(matches){
set_new(matches[1], matches[2], matches[3] ? matches[3].split(",") : [], overwrite);
Common.execute(callback);
}else if(Check.is_json(inputs))
self.add(JSON.parse(inputs), overwrite, callback);
else
anp.files.load_json(inputs, data => {
self.add(data, overwrite, callback);
});
}else if(Check.is_array(inputs)){
if(inputs.length >= 2 && inputs.length <= 3 && (
Check.is_string(inputs[0]) && inputs[0] == inputs[0].trim() &&
Check.is_key(inputs[1]) &&
!inputs[2] || (Check.is_array(inputs[2]) && inputs[2].every(Check.is_key))
)){
set_new(inputs[0], inputs[1], inputs[3], overwrite);
Common.execute(callback);
return;
};
/** @type {number} */
let i = 0;
/** @type {number} */
const l = inputs.length,
/** @type {default_callback} */
end = () => ++ i == l && Common.execute(callback);
inputs.forEach(subinputs => {
self.add(subinputs, overwrite, end);
});
}else if(Check.is_dictionary(inputs)){
set_new(inputs.route, inputs.view, inputs.permissions || [], overwrite);
Common.execute(callback);
}else
Common.execute(callback);
};
/**
* @param {!string} path
* @returns {void}
* @access public
*/
this.go = path => {
const main = anp.item_self.querySelector("main");
if(!main)
return;
main.innerHTML = "";
for(const route of routes){
/** @type {[boolean, Object.<string, string>]} */
const [ok, variables] = route.check(path);
if(ok){
Common.HTML("main", anp.views.get(route.view, variables))
break;
};
};
};
constructor(); constructor();
}; };

View File

@ -51,7 +51,7 @@ export const ViewsManager = (function(){
*/ */
this.update = (callback = null) => { this.update = (callback = null) => {
Common.execute_array(["default_views_files", "views_files", "default_views", "views"], (key, next) => { Common.execute_array(["default_views_files", "views_files", "default_views", "views"], (key, next) => {
self.add(self.get(key), true, next); self.add(anp.settings.get(key), true, next);
}, callback, true); }, callback, true);
}; };
@ -129,7 +129,17 @@ export const ViewsManager = (function(){
}, true); }, true);
}; };
this.get = (key, inputs = null) => {}; /**
* @param {!string} key
* @param {?(Object.<string, any|null>|Array.<any|null>)} [inputs = null]
* @returns {Array.<any|null>|null}
* @access public
*/
this.get = (key, inputs = null) => {
if(views[key] && views[key].type && anp.components[views[key].type])
return anp.components[views[key].type]([views[key], inputs]);
return anp.components.http_error(502);
};
constructor(); constructor();
}; };

View File

@ -2,12 +2,14 @@
import {Check} from "../Utils/Checks.ecma.js"; import {Check} from "../Utils/Checks.ecma.js";
import {Common} from "../Utils/Common.ecma.js"; import {Common} from "../Utils/Common.ecma.js";
import {RE} from "../Utils/Patterns.ecma.js";
/** /**
* @class RouteModel * @class RouteModel
* @constructor * @constructor
* @param {!(string|RegExp)} route * @param {!(string|RegExp)} route
* @param {!string} view * @param {!string} view
* @param {Array.<string>} [permissions = []]
* @returns {void} * @returns {void}
* @access public * @access public
* @static * @static
@ -18,11 +20,12 @@ export const RouteModel = (function(){
* @constructs RouteModel * @constructs RouteModel
* @param {!(string|RegExp)} route * @param {!(string|RegExp)} route
* @param {!string} view * @param {!string} view
* @param {Array.<string>} [permissions = []]
* @returns {void} * @returns {void}
* @access private * @access private
* @static * @static
*/ */
const RouteModel = function(route, view){ const RouteModel = function(route, view, permissions = []){
/** @type {RouteModel} */ /** @type {RouteModel} */
const self = this; const self = this;
@ -33,6 +36,8 @@ export const RouteModel = (function(){
this.variables = []; this.variables = [];
/** @type {string} */ /** @type {string} */
this.view = view; this.view = view;
/** @type {Array.<strings>} */
this.permissions = permissions;
/** /**
* @returns {void} * @returns {void}
@ -40,17 +45,35 @@ export const RouteModel = (function(){
*/ */
const constructor = () => { const constructor = () => {
if(Check.is_string(route)) if(Check.is_string(route))
route = new RegExp("^" + Common.to_regular_expression(route.replace(RE.STRING_VARIABLES, (_, key) => { self.route = new RegExp("^" + Common.to_regular_expression(route.replace(RE.STRING_VARIABLES, (_, key) => {
self.variables.push(key); self.variables.push(key);
return "#######"; return "#######";
})).replace(/#{7}/g, "([^\\/]+)") + "\\/?$", "i"); })).replace(/#{7}/g, "([^\\/]+)") + "\\/?$", "i");
else if(Check.is_regular_expression(route)) else if(Check.is_regular_expression(route))
(this.route = route).source.replace(/\([^\(\)]+\)/g, all => { (self.route = route).source.replace(/\([^\(\)]+\)/g, all => {
self.variables.push("" + self.variables.length); self.variables.push("" + self.variables.length);
return all; return all;
}); });
}; };
/**
* @param {!string} url
* @returns {[!boolean, Object.<string, string>]}
* @access public
*/
this.check = url => {
/** @type {RegExpMatchArray} */
const matches = url.match(self.route);
return matches ? [true, self.variables.reduce((variables, key, i) => {
variables[key] = matches[i + 1];
return variables;
}, {})] : [false, {}];
}
constructor(); constructor();
}; };

View File

@ -28,6 +28,32 @@ export const MarkDown = (function(){
MarkDown.Italic = () => new MarkDown.Item(/\*((?:(?!(?:\*)).|\*{2})+)\*(?!\*)/, (match, content) => `<i>${MarkDown.to_html(content, false)}</i>`); MarkDown.Italic = () => new MarkDown.Item(/\*((?:(?!(?:\*)).|\*{2})+)\*(?!\*)/, (match, content) => `<i>${MarkDown.to_html(content, false)}</i>`);
// MarkDown.UnorderedList = () => new MarkDown.Item(/^\s*[-+*]\s+(.+)$/m, (match, content) => `<ul><li>${MarkDown.to_html(content, false)}</li></ul>`, true);
// MarkDown.OrderedList = () => new MarkDown.Item(/^(\s*\d+\.\s+.+(?:\r\n|[\r\n])?)$/m, (match, content) => `<ol><li>${MarkDown.to_html(content, false)}</li></ol>`, true);
// MarkDown.Table = () => new MarkDown.Item(/^\s*\|(.+)\|\s*$/m, (match, content) => {
// const rows = content.split(/\r?\n/).map(row => row.trim().split("|").map(cell => cell.trim()));
// if(rows.length < 2)
// return content;
// const header = rows[0],
// alignments = rows[1].map(cell => {
// if(/^:-+:$/.test(cell))
// return "center";
// else if(/^-+:$/.test(cell))
// return "right";
// else if(/^:-+$/.test(cell))
// return "left";
// return null;
// }),
// body = rows.slice(2);
// return `<table><thead><tr>${header.map((cell, i) => `<th${alignments[i] ? ` style="text-align:${alignments[i]}"` : ""}>${MarkDown.to_html(cell, false)}</th>`).join("")}</tr></thead><tbody>${body.map(row => `<tr>${row.map((cell, i) => `<td${alignments[i] ? ` style="text-align:${alignments[i]}"` : ""}>${MarkDown.to_html(cell, false)}</td>`).join("")}</tr>`).join("")}</tbody></table>`;
// }, true);
MarkDown.to_html = (string, blocks = true) => { MarkDown.to_html = (string, blocks = true) => {
let html = ``; let html = ``;

View File

@ -8,5 +8,6 @@ export const RE = {
ON_ATTRIBUTE : /^on[\-_]?/i, ON_ATTRIBUTE : /^on[\-_]?/i,
SPECIAL_HTML_EVENT_CHARACTERS : /[^a-z0-9]+/gi, SPECIAL_HTML_EVENT_CHARACTERS : /[^a-z0-9]+/gi,
WHITE_SPACES : /(?:\s+|[\r\n]+)+/g, WHITE_SPACES : /(?:\s+|[\r\n]+)+/g,
TO_REGULAR_EXPRESSION : /[\.\-\^\$\\\/\[\]\(\)\{\}\!\?\*\+\n\r\t]/g TO_REGULAR_EXPRESSION : /[\.\-\^\$\\\/\[\]\(\)\{\}\!\?\*\+\n\r\t]/g,
ROUTES : /^([^ ]+) +([a-z_][a-z0-9_]*)(?: +([^ ]+)?)?$/i
}; };

View File

@ -0,0 +1,3 @@
[
"/ai ai"
]

View File

@ -33,5 +33,10 @@
}, },
"default_controllers" : { "default_controllers" : {
"ai" : "AIController" "ai" : "AIController"
} },
"default_views_files" : [
"/json/views/AnP.views.ai.json",
"/json/views/AnP.views.sessions.json"
],
"default_routes_files" : "/json/AnP.routes.json"
} }

View File

@ -31,6 +31,7 @@
"status" : "Estado", "status" : "Estado",
"raw_html" : "Código HTML", "raw_html" : "Código HTML",
"minimum" : "Mínimo", "minimum" : "Mínimo",
"maximum" : "Máximo" "maximum" : "Máximo",
"ai" : "IA"
} }
} }

View File

@ -0,0 +1,9 @@
{
"ai" : {
"type" : "aichat",
"name" : "anpbot",
"web_socket" : "anp",
"controller" : "ai",
"method" : "message"
}
}

View File

@ -1233,6 +1233,10 @@
content: "\f841"; content: "\f841";
font-family: "FA6FB"; font-family: "FA6FB";
} }
.anp [data-icon=ai]::before {
content: "\f27a";
font-family: "FA6FR";
}
.anp [data-icon=user]::before { .anp [data-icon=user]::before {
content: "\f007"; content: "\f007";
} }
@ -1298,10 +1302,10 @@
} }
.anp [data-done=true] [data-icon=done]::before { .anp [data-done=true] [data-icon=done]::before {
content: "\f058"; content: "\f058";
font-family: "FA6FR";
} }
.anp [data-done=false] [data-icon=done]::before { .anp [data-done=false] [data-icon=done]::before {
content: "\f071"; content: "\f057";
font-family: "FA6FR";
} }
.anp [data-icon=date_from]::before { .anp [data-icon=date_from]::before {
content: "\f783"; content: "\f783";

File diff suppressed because one or more lines are too long

View File

@ -10,6 +10,7 @@
} }
[data-icon=home]::before{content : unicode("f015");} [data-icon=home]::before{content : unicode("f015");}
[data-icon=git]::before{content : unicode("f841"); font-family : $FA6FB;} [data-icon=git]::before{content : unicode("f841"); font-family : $FA6FB;}
[data-icon=ai]::before{content : unicode("f27a"); font-family : $FA6FR;}
[data-icon=user]::before{content : unicode("f007");} [data-icon=user]::before{content : unicode("f007");}
[data-icon=ip]::before{content : "IP"; font-weight : 900; font-family : $font-normal;} [data-icon=ip]::before{content : "IP"; font-weight : 900; font-family : $font-normal;}
[data-icon=login]::before{content : unicode("f090");} [data-icon=login]::before{content : unicode("f090");}
@ -31,8 +32,8 @@
[data-icon=waiting]::before{content : unicode("f252");} [data-icon=waiting]::before{content : unicode("f252");}
[data-ok=true] [data-icon=ok]::before{content : unicode("f00c");} [data-ok=true] [data-icon=ok]::before{content : unicode("f00c");}
[data-ok=false] [data-icon=ok]::before{content : unicode("f00d");} [data-ok=false] [data-icon=ok]::before{content : unicode("f00d");}
[data-done=true] [data-icon=done]::before{content : unicode("f058"); font-family : $FA6FR;} [data-done=true] [data-icon=done]::before{content : unicode("f058");}
[data-done=false] [data-icon=done]::before{content : unicode("f071");} [data-done=false] [data-icon=done]::before{content : unicode("f057"); font-family : $FA6FR;}
[data-icon=date_from]::before{content : unicode("f783");} [data-icon=date_from]::before{content : unicode("f783");}
[data-icon=date_to]::before{content : unicode("f1da");} [data-icon=date_to]::before{content : unicode("f1da");}
[data-icon=time]::before{content : unicode("f017"); font-family : $FA6FR;} [data-icon=time]::before{content : unicode("f017"); font-family : $FA6FR;}

View File

View File

View File

View File

View File

View File

View File

View File

View File

@ -36,7 +36,7 @@ class ControllersManager:
for subinputs in self.anp.files.load_json(inputs, True): for subinputs in self.anp.files.load_json(inputs, True):
for key, controller in subinputs.items(): for key, controller in subinputs.items():
if Common.is_mark_key(key) and controller is None: if Check.is_mark_key(key) and controller is None:
continue continue
ControllerClass:type[ControllerAbstract]|None ControllerClass:type[ControllerAbstract]|None

View File

@ -35,7 +35,7 @@ class DispatchesManager:
for subinputs in self.anp.files.load_json(inputs, True): for subinputs in self.anp.files.load_json(inputs, True):
for key, dispatch in subinputs.items(): for key, dispatch in subinputs.items():
if Common.is_mark_key(key) and dispatch is None: if Check.is_mark_key(key) and dispatch is None:
continue continue
DispatchClass:type[DispatchAbstract]|None DispatchClass:type[DispatchAbstract]|None

View File

@ -93,7 +93,7 @@ class I18NManager:
if language not in self.__sentences: if language not in self.__sentences:
self.__sentences[language] = {} self.__sentences[language] = {}
for key, sentence in sentences.items(): for key, sentence in sentences.items():
if Common.is_mark_key(key) and sentence is None: if Check.is_mark_key(key) and sentence is None:
continue continue
if overwrite or key not in self.__sentences[language]: if overwrite or key not in self.__sentences[language]:
self.__sentences[language][key] = sentence self.__sentences[language][key] = sentence

View File

@ -5,6 +5,7 @@ from typing import Self, Any, Sequence, TypeVar
from Interfaces.Application.AnPInterface import AnPInterface from Interfaces.Application.AnPInterface import AnPInterface
from Abstracts.ModelAbstract import ModelAbstract from Abstracts.ModelAbstract import ModelAbstract
from Utils.Common import Common from Utils.Common import Common
from Utils.Checks import Check
T = TypeVar("T", bound = ModelAbstract) T = TypeVar("T", bound = ModelAbstract)
@ -48,7 +49,7 @@ class ModelsManager:
for subinputs in self.anp.files.load_json(inputs, True): for subinputs in self.anp.files.load_json(inputs, True):
for key, model in subinputs.items(): for key, model in subinputs.items():
if Common.is_mark_key(key) and model is None: if Check.is_mark_key(key) and model is None:
continue continue
if issubclass(model, ModelAbstract) and ( if issubclass(model, ModelAbstract) and (
overwrite or key not in self.__models overwrite or key not in self.__models

View File

@ -4,6 +4,7 @@
from typing import Any, Optional, Self, Sequence from typing import Any, Optional, Self, Sequence
from Interfaces.Application.AnPInterface import AnPInterface from Interfaces.Application.AnPInterface import AnPInterface
from Utils.Common import Common from Utils.Common import Common
from Utils.Checks import Check
class SettingsManager: class SettingsManager:
@ -58,7 +59,7 @@ class SettingsManager:
value:Any|None value:Any|None
for key, value in subinputs.items(): for key, value in subinputs.items():
if Common.is_mark_key(key) and value is None: if Check.is_mark_key(key) and value is None:
continue continue
if overwrite or key not in self.__settings: if overwrite or key not in self.__settings:
self.__settings[key] = value self.__settings[key] = value
@ -73,7 +74,7 @@ class SettingsManager:
value:Any|None value:Any|None
for key, value in subinputs.items(): for key, value in subinputs.items():
if Common.is_mark_key(key) and value is None: if Check.is_mark_key(key) and value is None:
continue continue
if overwrite or key not in self.__secrets: if overwrite or key not in self.__secrets:
self.__secrets[key] = value self.__secrets[key] = value

View File

0
Python/Models/__init__py Normal file
View File

View File

@ -1,8 +1,9 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from typing import Any, Self from typing import Any, Self, Sequence
from re import Pattern as REPattern from re import Pattern as REPattern
from os.path import isfile as is_file
from json import loads as json_decode from json import loads as json_decode
from Utils.Patterns import RE from Utils.Patterns import RE
@ -66,4 +67,21 @@ class Check:
return True return True
if Check.is_string(item): if Check.is_string(item):
return Check.is_json_string(item, full) return Check.is_json_string(item, full)
return False return False
@classmethod
def is_mark_key(cls:type[Self], key:str, marks:str|Sequence[str] = "AnP") -> bool:
mark:str
for mark in cls.get_keys(marks):
if key.startswith(mark + "_") and (
key.endswith("_start") or
key.endswith("_end")
):
return True
return False
@staticmethod
def is_file(path:str) -> bool:
return is_file(path)

View File

@ -3,7 +3,7 @@
from typing import Any, Callable, Optional, Sequence, Self from typing import Any, Callable, Optional, Sequence, Self
from re import Match as REMatch from re import Match as REMatch
from os.path import abspath as absolute_path, dirname as directory_name, exists as path_exists, isfile as is_file from os.path import abspath as absolute_path, dirname as directory_name, exists as path_exists
from json import loads as json_decode from json import loads as json_decode
from io import FileIO from io import FileIO
from mimetypes import guess_type as get_mime_by_extension from mimetypes import guess_type as get_mime_by_extension
@ -264,23 +264,6 @@ class Common:
# return "font/ttf" # return "font/ttf"
return get_mime_by_extension(path)[0] return get_mime_by_extension(path)[0]
@classmethod
def is_mark_key(cls:type[Self], key:str, marks:str|Sequence[str] = "AnP") -> bool:
mark:str
for mark in cls.get_keys(marks):
if key.startswith(mark + "_") and (
key.endswith("_start") or
key.endswith("_end")
):
return True
return False
@staticmethod
def is_file(path:str) -> bool:
return is_file(path)
@staticmethod @staticmethod
def json_encode(data:dict[str, Any|None]|Sequence[Any|None]) -> str|None: def json_encode(data:dict[str, Any|None]|Sequence[Any|None]) -> str|None:
try: try:

0
Python/Utils/__init__py Normal file
View File

0
Python/__init__py Normal file
View File

312
Python/map.anp.py Normal file
View File

@ -0,0 +1,312 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from abc import ABC, abstractmethod
from typing import Any, Callable, Self, Sequence, Optional, ClassVar
from re import Pattern as REPattern
from threading import Thread
class RE:
KEY:ClassVar[REPattern]
STRING_VARIABLES:ClassVar[REPattern]
SLASHES:ClassVar[REPattern]
NEW_LINES:ClassVar[REPattern]
ROUTE:ClassVar[REPattern]
TO_REGULAR_EXPRESSION:ClassVar[REPattern]
ROUTE_KEY:ClassVar[REPattern]
EXCEPTION:ClassVar[REPattern]
NEW_LINE:ClassVar[REPattern]
HTTP_VARIABLE:ClassVar[REPattern]
PARENT_PATH:ClassVar[REPattern]
DOUBLE_NEW_LINE:ClassVar[REPattern]
HTTP_HEADER_PARAMETER:ClassVar[REPattern]
HTTP_REQUEST:ClassVar[REPattern]
class Check(ABC):
@staticmethod
@abstractmethod
def is_string(item:Any|None) -> bool:pass
@staticmethod
@abstractmethod
def is_binary(item:Any|None) -> bool:pass
@classmethod
@abstractmethod
def is_key(cls:type[Self], item:Any|None) -> bool:pass
@staticmethod
@abstractmethod
def is_array(item:Any|None) -> bool:pass
@staticmethod
@abstractmethod
def is_dictionary(item:Any|None) -> bool:pass
@staticmethod
@abstractmethod
def is_boolean(item:Any|None) -> bool:pass
@staticmethod
@abstractmethod
def is_integer(item:Any|None) -> bool:pass
@staticmethod
@abstractmethod
def is_function(item:Any|None) -> bool:pass
@staticmethod
@abstractmethod
def is_regular_expression(item:Any|None) -> bool:pass
@staticmethod
@abstractmethod
def is_json_string(item:Any|None, full:bool = False) -> bool:pass
@staticmethod
@abstractmethod
def is_json(item:Any|None) -> bool:pass
@staticmethod
@abstractmethod
def is_json_data(item:Any|None, full:bool = False) -> bool:pass
@classmethod
@abstractmethod
def is_mark_key(cls:type[Self], key:str, marks:str|Sequence[str] = "AnP") -> bool:pass
@staticmethod
@abstractmethod
def is_file(path:str) -> bool:pass
class Common(ABC):
ROOT_PATH:ClassVar[str]
SLASH:ClassVar[str]
ROOTS_PATH:ClassVar[list[str]]
SPECIAL_REGULAR_EXPRESSION_CHARACTERS:ClassVar[dict[str, str]]
@classmethod
@abstractmethod
def get_keys(cls:type[Self], *items:list[Any|None]) -> list[str]:pass
@classmethod
@abstractmethod
def get_texts(cls:type[Self], *items:list[Any|None]) -> list[str]:pass
@classmethod
@abstractmethod
def get_dictionaries(cls:type[Self], *items:list[Any|None]) -> list[dict[str, Any|None]]:pass
@classmethod
@abstractmethod
def get_dictionary(cls:type[Self], inputs:Any|None, overwrite:bool = False) -> dict[str, Any|None]:pass
@classmethod
@abstractmethod
def get_value(cls:type[Self],
keys:str|Sequence[str],
inputs:dict[str, Any|None]|Sequence[Any|None],
default:Optional[Any|None] = None
) -> Any|None:pass
@classmethod
@abstractmethod
def string_variables(cls:type[Self],
string:str,
inputs:dict[str, Any|None]|Sequence[Any|None],
default:Optional[str] = None
) -> str:pass
@classmethod
@abstractmethod
def fix_path(cls:type[Self], path:str) -> str:pass
@classmethod
@abstractmethod
def get_absolute_path(cls:type[Self], path:str) -> str|None:pass
@classmethod
@abstractmethod
def load_file(cls:type[Self], path:str, mode:str = "r") -> str|bytes|None:pass
@classmethod
@abstractmethod
def load_json(cls:type[Self],
data:str|dict[str, Any|None]|Sequence[Any|None],
only_dictionaries:bool = True
) -> list[dict[str, Any|None]|Sequence[Any|None]]:pass
@staticmethod
@abstractmethod
def get_action_data(i:int = 0) -> dict[str, str|int]:pass
@classmethod
@abstractmethod
def to_regular_expression(cls:type[Self], string:str) -> str:pass
@staticmethod
@abstractmethod
def get_mime_from_path(path:str) -> str|None:pass
@staticmethod
@abstractmethod
def json_encode(data:dict[str, Any|None]|Sequence[Any|None]) -> str|None:pass
@staticmethod
@abstractmethod
def json_decode(data:str|bytes) -> dict[str, Any|None]|Sequence[Any|None]|None:pass
@staticmethod
@abstractmethod
def base64_encode(data:bytes) -> str|None:pass
@staticmethod
@abstractmethod
def base64_decode(data:str) -> bytes|None:pass
@classmethod
@abstractmethod
def data_encode(cls:type[Self], data:Any|None) -> str|None:pass
@classmethod
@abstractmethod
def data_decode(cls:type[Self], data:str) -> Any|None:pass
@staticmethod
@abstractmethod
def execute(callback:Callable[..., Any|None], *arguments:Any|None) -> Any|None:pass
@staticmethod
@abstractmethod
def unique(items:str|Sequence[Any|None]) -> str|list[Any|None]:pass
@staticmethod
@abstractmethod
def random(items:str|Sequence[Any|None]) -> Any|None:pass
class AIResponseModel(ABC):
def __init__(self:Self, conversation:str) -> None:
self.conversation:str
self.start:float
self.model:str
self.response:str
self.total:str
self.context:str|list[str]
self.done:bool
self.end:float|None
self.chunks:int
self.ok:bool
self.http_code:int
self.http_message:str
@abstractmethod
def update(self:Self, data:dict[str, Any|None]) -> None:pass
class CommandModel(ABC):
def __init__(self:Self,
names:str|Sequence[str],
callback:Callable[[dict[str, Any|None], Optional[list[Any|None]]], None]
) -> None:
self.names:list[str]
self.callback:Callable[[dict[str, Any|None], Optional[list[Any|None]]], None]
@abstractmethod
def update(self:Self,
names:str|Sequence[str],
callback:Optional[Callable[[dict[str, Any|None], Optional[list[Any|None]]], None]] = None,
overwrite:bool = False
) -> None:pass
class PseudoLoRAModel(ABC):
def __init__(self:Self,
title:str,
content:str|Sequence[Any|None],
keys:Optional[str|Sequence[str]] = None,
cacheable:bool = False
) -> None:
self.title:str
self.path:str|None
self.memory:int
self.cache:str
self.i:int
self.keys:list[str]
self.cacheable:bool
self.nested:list[PseudoLoRAModel]
@abstractmethod
def clean_cache(self:Self) -> None:pass
class QueueItemModel(ABC):
def __init__(self:Self, i:int, callback:Callable[[Callable[[], None]], None], *arguments:list[Any|None]) -> None:
self.i:int
self.callback:Callable[[Callable[[], None]], None]
self.arguments:list[Any|None]
self.executing:bool
self.thread:Thread|None
class QueuesModel(ABC):
def __init__(self:Self, key:str, inputs:Optional[int|dict[str, Any|None]|Sequence[Any|None]] = None) -> None:
self.key:str
self.items:dict[int, QueueItemModel]
self.i:int
self.p:int
self.maximum:int
self.current:int
@abstractmethod
def add(self:Self, callback:Callable[[Callable[[], None]], None], *arguments:list[Any|None]) -> int|None:pass
@abstractmethod
def cancel(self:Self, i:int) -> bool:pass
@abstractmethod
def cancel_all(self:Self) -> None:pass
@abstractmethod
def next(self:Self) -> None:pass
class RequestModel(ABC):
def __init__(self:Self, session:Optional[SessionModel] = None) -> None:
self.post_variables:dict[str, Any|None]
self.get_variables:dict[str, Any|None]
self.url_variables:dict[str, Any|None]
self.hash_variables:dict[str, Any|None]
self.cookies:dict[str, Any|None]
self.variables:dict[str, Any|None]
self.request_headers:dict[str, Any|None]
self.method:str|None
self.route:RouteAbstract|None
self.response:str|bytes|None
self.response_mime:str|None
self.response_charset:str|None
self.response_code:int
self.response_headers:dict[str, Any|None]
self.callback:Callable[[RequestModel, str|bytes|None], None]|None
self.data:Any|None
self.session:SessionModel|None
self.protocol:str|None
self.protocol_version:str|None
self.last_modified:str|None
@abstractmethod
def get(self:Self, key:str|Sequence[str], default:Optional[Any] = None) -> Any|None:pass
@abstractmethod
def set_variables(self:Self, inputs:dict[str, Any|None], on:Optional[str] = None) -> None:pass
@abstractmethod
def set_response(self:Self, data:Any|None) -> None:pass
@abstractmethod
def set_request_headers(self:Self, inputs:Any|None) -> None:pass
@abstractmethod
def set_response_headers(self:Self, inputs:Any|None) -> None:pass