ranui
Version:
A framework-agnostic Web Components UI library built on native custom elements, with TypeScript types, light/dark theming, SSR and PWA support.
307 lines (306 loc) • 10.5 kB
JavaScript
import { T as N, _ as f, i as g, m as b, n as $ } from "./factory-WDxu11V0.js";
import "./builder-EH76YCFS.js";
import { escapeHtml as I } from "ranuts/utils";
import { Marked as _ } from "marked";
import S from "dompurify";
import he from "remend";
var u = null, T = (t) => {
if (typeof document > "u") return !1;
u || (u = document.createElement("template")), u.innerHTML = `${t}<i data-ran-probe=""></i>`;
const e = u.content.querySelector("[data-ran-probe]"), r = e !== null && e.parentNode !== u.content;
return u.innerHTML = "", r;
}, O = (t) => {
const e = [];
let r = "";
for (const n of t) {
const a = e[e.length - 1];
if (r) {
a.push(n), n.type === "html" && (r += n.raw, T(r) || (r = ""));
continue;
}
if (n.type === "space" || n.type === "def") {
a && a.push(n);
continue;
}
e.push([n]), n.type === "html" && n.block && T(n.raw) && (r = n.raw);
}
return e;
}, P = /^[ \t]{0,3}(`{3,}|~{3,})/, H = (t) => {
let e = null, r = 0;
for (const n of t.split(`
`)) {
const a = P.exec(n);
e === null ? a && (e = a[1][0], r = a[1].length) : a && a[1][0] === e && a[1].length >= r && (e = null, r = 0);
}
return e !== null;
}, v = null, w = null, h = null, k = /* @__PURE__ */ new Set(), M = /* @__PURE__ */ new Set(), p = /* @__PURE__ */ new Map(), D = () => h || (h = Promise.all([import("shiki"), import("shiki/engine/javascript")]).then(async ([t, e]) => {
v = t;
const r = await t.createHighlighter({
themes: [],
langs: [],
engine: e.createJavaScriptRegexEngine({ forgiving: !0 })
});
return w = r, r;
}), h), E = (t) => {
const e = (t || "").trim().toLowerCase();
return !e || e === "text" || e === "plain" || e === "plaintext" || e === "txt" ? "text" : v ? e in v.bundledLanguages ? e : "text" : e;
}, m = (t, e) => {
const r = `${t}:${e}`, n = t === "theme" ? k : M;
if (n.has(e)) return Promise.resolve();
const a = p.get(r);
if (a) return a;
const s = D().then(async (o) => {
t === "theme" ? await o.loadTheme(e) : e !== "text" && await o.loadLanguage(e), n.add(e);
}).finally(() => p.delete(r));
return p.set(r, s), s;
}, B = (t, e) => !!w && k.has(e.light) && k.has(e.dark) && (t === "text" || M.has(t)), A = (t, e, r) => w.codeToHtml(t, {
lang: e,
themes: {
light: r.light,
dark: r.dark
},
defaultColor: !1
}), W = (t, e, r) => {
const n = E(e);
return B(n, r) ? A(t, n, r) : Promise.all([m("theme", r.light), m("theme", r.dark)]).then(() => {
const a = E(e);
return m("lang", a).then(() => A(t, a, r));
}).catch(() => null);
}, F = {
light: "github-light",
dark: "github-dark"
}, pe = (t) => {
const e = t.trim().split(/\s+/).filter(Boolean);
return e.length === 0 ? F : e.length === 1 ? {
light: e[0],
dark: e[0]
} : {
light: e[0],
dark: e[1]
};
}, L = (t, e) => `<span class="ran-md-math" data-ran-math="${I(t)}" data-display="${e}"></span>`, y = (t, e, r, n) => {
if (!t.startsWith(e)) return null;
const a = e.length, s = t.indexOf(r, a);
if (s === -1 || s === a) return null;
let o = s + r.length;
if (n) {
if (o !== t.length && t[o] !== `
`) return null;
for (; t[o] === `
`; ) o += 1;
}
return {
raw: t.slice(0, o),
text: t.slice(a, s).trim()
};
}, U = {
name: "ranMathBlock",
level: "block",
start(t) {
const e = t.indexOf("$$"), r = t.indexOf("\\[");
return e === -1 ? r === -1 ? void 0 : r : r === -1 ? e : Math.min(e, r);
},
tokenizer(t) {
const e = y(t, "$$", "$$", !0) || y(t, "\\[", "\\]", !0);
if (e)
return {
type: "ranMathBlock",
raw: e.raw,
text: e.text
};
},
renderer(t) {
return `<p class="ran-md-math-block">${L(t.text, "block")}</p>
`;
}
}, G = (t) => {
for (let e = 0; e < t.length; e += 1)
if (t[e] === "$") {
if (t[e + 1] === "$") {
e += 1;
continue;
}
if (!(e > 0 && t[e - 1] === "$"))
return e;
}
return -1;
}, z = /^\$(?!\s)((?:\\.|[^$\\\n])+?)(?<!\s)\$(?!\d)/, J = (t) => ({
name: "ranMathInline",
level: "inline",
start(e) {
const r = e.indexOf("\\("), n = t ? G(e) : -1;
return r === -1 ? n === -1 ? void 0 : n : n === -1 ? r : Math.min(r, n);
},
tokenizer(e) {
const r = y(e, "\\(", "\\)", !1);
if (r) return {
type: "ranMathInline",
raw: r.raw,
text: r.text
};
const n = t ? z.exec(e) : null;
if (n)
return {
type: "ranMathInline",
raw: n[0],
text: n[1].trim()
};
},
renderer(e) {
return L(e.text, "inline");
}
}), x = /* @__PURE__ */ new Map(), Y = (t) => {
const e = t ? "dollar" : "default";
let r = x.get(e);
return r || (r = new _({
gfm: !0,
breaks: !1,
async: !1
}), r.use({ extensions: [U, J(t)] }), x.set(e, r)), r;
}, q = {
USE_PROFILES: { html: !0 },
FORBID_TAGS: [
"style",
"form",
"textarea",
"select",
"button",
"iframe",
"object",
"embed"
],
FORBID_ATTR: ["style"],
ADD_ATTR: ["target"],
RETURN_DOM_FRAGMENT: !0
}, K = "\0", j = (t, e) => {
let r = "", n = !1;
for (const s of t)
r += s.raw, s.type === "table" && (n = !0);
const a = t[t.length - 1];
return {
tokens: t,
raw: r,
key: e ? `${r}${K}${e}` : r,
hasTable: n,
incompleteCode: a?.type === "code" && H(a.raw)
};
}, me = (t, e = {}) => {
const r = Y(!!e.inlineMath), n = (o) => S.sanitize(r.parser(o.tokens), q);
if (!t.trim()) return {
blocks: [],
render: n
};
const a = r.lexer(t);
let s = "";
for (const o of a) o.type === "def" && (s += o.raw);
return {
blocks: (e.split === !1 ? [Array.from(a)] : O(a)).map((o) => j(o, s)),
render: n
};
}, V = "language-", X = (t) => {
const e = Array.from(t.classList).find((n) => n.startsWith(V)), r = t.textContent || "";
return {
lang: e ? e.slice(9).toLowerCase() : "",
code: r.endsWith(`
`) ? r.slice(0, -1) : r
};
}, C = (t, e, r, n) => {
const a = N();
return $().class("ran-md-btn").part("button").attr("type", "button").data("md-action", t).aria("label", r).attr("title", r).children(f("r-icon").attr("name", e).attr("size", "16").attr("color", "currentColor").ref(a)).on("click", () => n(a.current)).build();
}, Q = (t) => {
const e = t.split(`
`);
return e.length > 1 && e[e.length - 1] === "" && e.pop(), e.flatMap((r, n) => {
const a = b().class("line").text(r).build();
return n === 0 ? [a] : [document.createTextNode(`
`), a];
});
}, Z = (t) => f("pre").class("ran-md-pre").children(f("code").children(...Q(t))).build(), ee = (t, e, r, n, a, s) => {
const o = W(r, n, a), d = (c) => {
if (!c || !t.isConnected || Number(t.dataset.renderToken) !== s) return;
const l = document.createElement("template");
l.innerHTML = c;
const i = l.content.firstElementChild;
!i || i.tagName !== "PRE" || (i.classList.add("ran-md-pre"), i.removeAttribute("style"), i.removeAttribute("tabindex"), e.pre.replaceWith(i), e.pre = i);
};
typeof o == "string" ? d(o) : o.then(d);
}, te = 0, re = (t, e) => f("r-mermaid").class("ran-md-mermaid").attr("code", encodeURIComponent(t.trim())).attr("theme", e.theme).boolAttr("copy", e.copy).boolAttr("download", e.download).boolAttr("fullscreen", !0).build(), R = (t, e) => f("r-math").attr("latex", encodeURIComponent(t)).attr("display", e).build(), ne = (t, e) => {
const r = t.firstElementChild;
if (!r || r.tagName !== "CODE") return null;
const { lang: n, code: a } = X(r);
if (n === "mermaid" && !e.incomplete) {
import("./mermaid.js");
const i = re(a, e);
return t.replaceWith(i), i;
}
if ((n === "math" || n === "latex") && !e.incomplete)
return import("./math.js"), t.replaceWith(R(a.trim(), "block")), null;
const s = ++te, o = [];
e.copy && o.push(C("copy", "copy", e.labels.copy, (i) => e.onCopy(i, a, n))), e.download && o.push(C("download", "download", e.labels.download, () => e.onDownload(a, n)));
const d = n || o.length ? g().class("ran-md-code-header").part("code-header").children(b().class("ran-md-code-lang").part("code-lang").text(n), b().class("ran-md-code-actions").part("code-actions").children(...o)) : null, c = { pre: Z(a) }, l = g().class("ran-md-code").part("code").data("render-token", String(s)).children(d, c.pre).build();
return n && (l.dataset.language = n), e.incomplete && (l.dataset.incomplete = ""), e.lineNumbers && (l.dataset.lineNumbers = ""), t.replaceWith(l), e.highlight && ee(l, c, a, n, e.highlight, s), null;
}, ae = /[\u3000-\u303f\u3040-\u30ff\u3400-\u4dbf\u4e00-\u9fff\uac00-\ud7af\uf900-\ufaff\uff00-\uffef]/u, oe = /^(https?:\/\/|www\.)([^/?#]*)/, le = (t) => {
const e = t.textContent ?? "", r = t.getAttribute("href") ?? "";
let n = r;
try {
n = decodeURI(r);
} catch {
}
if (e !== n) return;
const a = oe.exec(e);
if (a === null) return;
const s = a[2].search(ae);
if (s < 0) return;
const o = e.slice(0, a[1].length + s);
t.textContent = o, t.setAttribute("href", o), t.after(document.createTextNode(e.slice(o.length)));
}, ge = (t, e) => {
const r = [], n = [], a = [], s = [], o = [], d = document.createTreeWalker(t, NodeFilter.SHOW_ELEMENT);
for (let l = d.nextNode(); l; l = d.nextNode()) {
const i = l;
switch (i.tagName) {
case "PRE":
r.push(i);
break;
case "A":
n.push(i);
break;
case "TABLE":
a.push(i);
break;
case "IMG":
s.push(i);
break;
case "SPAN":
i.dataset.ranMath !== void 0 && o.push(i);
}
}
const c = [];
for (const l of r) {
const i = ne(l, e);
i && c.push(i);
}
if (o.length > 0) {
import("./math.js");
for (const l of o) l.replaceWith(R(l.dataset.ranMath || "", l.dataset.display === "inline" ? "inline" : "block"));
}
for (const l of n) {
const i = l.getAttribute("href");
if (!i) {
l.dataset.incomplete = "";
continue;
}
i.startsWith("#") || (le(l), e.linkTarget && e.linkTarget !== "_self" && (l.setAttribute("target", e.linkTarget), l.setAttribute("rel", "noopener noreferrer")));
}
for (const l of a) {
const i = g().class("ran-md-table-wrap").part("table").build();
l.replaceWith(i), i.appendChild(l);
}
for (const l of s) l.hasAttribute("loading") || l.setAttribute("loading", "lazy");
return { themed: c };
};
export {
ge as enhance,
me as parseDocument,
pe as parseThemes,
he as remend
};