feat: universal LLM gateway with embedded multilingual admin console
- OpenAI-compatible gateway with intelligent routing via cortiq-router (allaigate) - Embedded build-free admin SPA at /admin: 7 languages (en/ru/de/fr/es/zh/tr), light/dark themes, hot config reload (no restart) - Admin API, per-request statistics + Prometheus /metrics, secret store, playground - English documentation (primary) with Russian translations (*.ru.md)
This commit is contained in:
Vendored
+56
@@ -0,0 +1,56 @@
|
||||
// Dependency-free SVG charts. Build-free, theme-aware via CSS classes.
|
||||
import { h } from "./ui.js";
|
||||
|
||||
const SVGNS = "http://www.w3.org/2000/svg";
|
||||
function s(tag, attrs = {}, ...children) {
|
||||
const el = document.createElementNS(SVGNS, tag);
|
||||
for (const [k, v] of Object.entries(attrs)) if (v != null) el.setAttribute(k, v);
|
||||
for (const c of children.flat()) if (c != null) el.appendChild(c);
|
||||
return el;
|
||||
}
|
||||
|
||||
// Area+line chart from time buckets. `field` is a key on each bucket.
|
||||
export function lineChart(buckets, field) {
|
||||
const W = 600, H = 180, padL = 8, padR = 8, padT = 12, padB = 14;
|
||||
const data = (buckets || []).map((b) => Number(b[field] || 0));
|
||||
const svg = s("svg", { class: "chart", viewBox: `0 0 ${W} ${H}`, preserveAspectRatio: "none" });
|
||||
// baseline + mid gridlines
|
||||
for (let i = 0; i <= 2; i++) {
|
||||
const y = padT + ((H - padT - padB) * i) / 2;
|
||||
svg.appendChild(s("line", { class: "axis", x1: padL, y1: y, x2: W - padR, y2: y }));
|
||||
}
|
||||
if (data.length < 2) return svg;
|
||||
const max = Math.max(1, ...data);
|
||||
const innerW = W - padL - padR, innerH = H - padT - padB;
|
||||
const x = (i) => padL + (innerW * i) / (data.length - 1);
|
||||
const y = (v) => padT + innerH - (innerH * v) / max;
|
||||
let line = "", area = `M ${x(0)} ${y(0)}`;
|
||||
data.forEach((v, i) => {
|
||||
const cmd = i === 0 ? "M" : "L";
|
||||
line += `${cmd} ${x(i).toFixed(1)} ${y(v).toFixed(1)} `;
|
||||
area += ` L ${x(i).toFixed(1)} ${y(v).toFixed(1)}`;
|
||||
});
|
||||
area += ` L ${x(data.length - 1)} ${y(0)} Z`;
|
||||
svg.appendChild(s("path", { class: "area", d: area }));
|
||||
svg.appendChild(s("path", { class: "line", d: line }));
|
||||
return svg;
|
||||
}
|
||||
|
||||
// Horizontal bar list. items: [{key, value, sub}]
|
||||
export function barList(items, fmt) {
|
||||
const max = Math.max(1, ...items.map((i) => i.value));
|
||||
return h(
|
||||
"div",
|
||||
{ class: "barlist" },
|
||||
items.length === 0 ? h("div", { class: "empty" }, "—") : null,
|
||||
...items.map((it) =>
|
||||
h(
|
||||
"div",
|
||||
{ class: "b" },
|
||||
h("div", { class: "mono", title: it.key }, it.key || "—"),
|
||||
h("div", { class: "track" }, h("i", { style: `width:${(it.value / max) * 100}%` })),
|
||||
h("div", { class: "val" }, fmt ? fmt(it.value) : it.value)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user