@gguf/claw
Version:
Multi-channel AI gateway with extensible messaging integrations
104 lines (103 loc) • 3.29 kB
JavaScript
//#region src/markdown/render.ts
const STYLE_RANK = new Map([
"blockquote",
"code_block",
"code",
"bold",
"italic",
"strikethrough",
"spoiler"
].map((style, index) => [style, index]));
function sortStyleSpans(spans) {
return [...spans].toSorted((a, b) => {
if (a.start !== b.start) return a.start - b.start;
if (a.end !== b.end) return b.end - a.end;
return (STYLE_RANK.get(a.style) ?? 0) - (STYLE_RANK.get(b.style) ?? 0);
});
}
function renderMarkdownWithMarkers(ir, options) {
const text = ir.text ?? "";
if (!text) return "";
const styleMarkers = options.styleMarkers;
const styled = sortStyleSpans(ir.styles.filter((span) => Boolean(styleMarkers[span.style])));
const boundaries = /* @__PURE__ */ new Set();
boundaries.add(0);
boundaries.add(text.length);
const startsAt = /* @__PURE__ */ new Map();
for (const span of styled) {
if (span.start === span.end) continue;
boundaries.add(span.start);
boundaries.add(span.end);
const bucket = startsAt.get(span.start);
if (bucket) bucket.push(span);
else startsAt.set(span.start, [span]);
}
for (const spans of startsAt.values()) spans.sort((a, b) => {
if (a.end !== b.end) return b.end - a.end;
return (STYLE_RANK.get(a.style) ?? 0) - (STYLE_RANK.get(b.style) ?? 0);
});
const linkStarts = /* @__PURE__ */ new Map();
if (options.buildLink) for (const link of ir.links) {
if (link.start === link.end) continue;
const rendered = options.buildLink(link, text);
if (!rendered) continue;
boundaries.add(rendered.start);
boundaries.add(rendered.end);
const openBucket = linkStarts.get(rendered.start);
if (openBucket) openBucket.push(rendered);
else linkStarts.set(rendered.start, [rendered]);
}
const points = [...boundaries].toSorted((a, b) => a - b);
const stack = [];
let out = "";
for (let i = 0; i < points.length; i += 1) {
const pos = points[i];
while (stack.length && stack[stack.length - 1]?.end === pos) {
const item = stack.pop();
if (item) out += item.close;
}
const openingItems = [];
const openingLinks = linkStarts.get(pos);
if (openingLinks && openingLinks.length > 0) for (const [index, link] of openingLinks.entries()) openingItems.push({
end: link.end,
open: link.open,
close: link.close,
kind: "link",
index
});
const openingStyles = startsAt.get(pos);
if (openingStyles) for (const [index, span] of openingStyles.entries()) {
const marker = styleMarkers[span.style];
if (!marker) continue;
openingItems.push({
end: span.end,
open: marker.open,
close: marker.close,
kind: "style",
style: span.style,
index
});
}
if (openingItems.length > 0) {
openingItems.sort((a, b) => {
if (a.end !== b.end) return b.end - a.end;
if (a.kind !== b.kind) return a.kind === "link" ? -1 : 1;
if (a.kind === "style" && b.kind === "style") return (STYLE_RANK.get(a.style) ?? 0) - (STYLE_RANK.get(b.style) ?? 0);
return a.index - b.index;
});
for (const item of openingItems) {
out += item.open;
stack.push({
close: item.close,
end: item.end
});
}
}
const next = points[i + 1];
if (next === void 0) break;
if (next > pos) out += options.escapeText(text.slice(pos, next));
}
return out;
}
//#endregion
export { renderMarkdownWithMarkers as t };