ranui
Version:
A framework-agnostic Web Components UI library built on native custom elements, with TypeScript types, light/dark theming, SSR and PWA support.
302 lines (301 loc) • 18 kB
JavaScript
import { T as x, _ as y, i as b } from "./factory-WDxu11V0.js";
import "./builder-EH76YCFS.js";
import { a as _, i as l, n, o as T, r as v, t as z } from "./utils-U-9uH7EX.js";
import { a as C, n as O } from "./ssr-BzykYefm.js";
import "./icon-DgLPjeVe.js";
var M = ':host{display:var(--ran-markdown-display, block);box-sizing:border-box;color:var(--ran-markdown-color, var(--ran-color-text, #171717));font-family:var(--ran-markdown-font-family, var(--ran-font-family, inherit));font-size:var(--ran-markdown-font-size, var(--ran-text-copy-1, 16px));line-height:var(--ran-markdown-line-height, 1.7);overflow-wrap:break-word;contain:inline-size;width:var(--ran-markdown-width, 100%);min-width:0;max-width:100%}:host([hidden]){display:none}.ran-markdown{position:relative}.ran-markdown-body{display:flex;flex-direction:column;gap:var(--ran-markdown-gap, var(--ran-space-4, 16px))}.ran-markdown-block{min-width:0;max-width:100%}.ran-markdown-block>:first-child{margin-top:0}.ran-markdown-block>:last-child{margin-bottom:0}:host([caret]) .ran-markdown-block:last-child:not([data-no-caret])>:last-child:after{content:var(--ran-markdown-caret, "▋");display:inline-block;margin-left:.15em;color:var(--ran-markdown-caret-color, var(--ran-color-text-secondary, #666));animation:ran-markdown-caret 1s steps(2,start) infinite}:host([caret="circle"]) .ran-markdown-block:last-child:not([data-no-caret])>:last-child:after{content:var(--ran-markdown-caret, "●");font-size:.6em;vertical-align:middle}@keyframes ran-markdown-caret{to{visibility:hidden}}h1,h2,h3,h4,h5,h6{margin:var(--ran-markdown-heading-margin, var(--ran-space-6, 32px) 0 var(--ran-space-3, 12px));color:var(--ran-markdown-heading-color, var(--ran-color-text, #171717));font-weight:var(--ran-markdown-heading-weight, var(--ran-text-heading-weight, 600));line-height:1.3;letter-spacing:-.01em}h1{font-size:var(--ran-markdown-h1-size, var(--ran-text-heading-1, 32px))}h2{font-size:var(--ran-markdown-h2-size, var(--ran-text-heading-2, 24px))}h3{font-size:var(--ran-markdown-h3-size, var(--ran-text-heading-3, 20px))}h4,h5,h6{font-size:var(--ran-markdown-h4-size, var(--ran-text-heading-4, 16px))}p{margin:0 0 var(--ran-markdown-paragraph-margin, var(--ran-space-3, 12px))}a{color:var(--ran-markdown-link-color, var(--ran-color-link, #0070f3));text-decoration:underline;text-underline-offset:.15em;text-decoration-color:var(--ran-markdown-link-underline, var(--ran-gray-alpha-500, currentColor))}a:hover{text-decoration-color:currentColor}a[data-incomplete]{color:inherit;text-decoration:none;cursor:text}strong{font-weight:var(--ran-markdown-strong-weight, 600)}del{color:var(--ran-color-text-secondary, #666)}hr{height:0;margin:var(--ran-space-6, 32px) 0;border:0;border-top:var(--ran-skin-border-width, 1px) var(--ran-skin-border-style, solid) var(--ran-markdown-hr-color, var(--ran-color-border, #eaeaea))}ul,ol{margin:0 0 var(--ran-markdown-paragraph-margin, var(--ran-space-3, 12px));padding-left:var(--ran-markdown-list-indent, var(--ran-space-6, 32px))}li{margin:var(--ran-markdown-list-item-margin, var(--ran-space-1, 4px)) 0}li>p{margin-bottom:var(--ran-space-2, 8px)}li>ul,li>ol{margin:var(--ran-space-1, 4px) 0 0}li:has(>input[type=checkbox]){list-style:none;margin-left:calc(-1 * var(--ran-space-5, 24px))}li>input[type=checkbox]{margin:0 var(--ran-space-2, 8px) 0 0;vertical-align:-.1em;accent-color:var(--ran-color-primary, #0070f3)}blockquote{margin:0 0 var(--ran-markdown-paragraph-margin, var(--ran-space-3, 12px));padding:var(--ran-space-1, 4px) 0 var(--ran-space-1, 4px) var(--ran-space-4, 16px);border-left:var(--ran-markdown-blockquote-border-width, 3px) solid var(--ran-markdown-blockquote-border, var(--ran-color-border-secondary, #d4d4d4));color:var(--ran-markdown-blockquote-color, var(--ran-color-text-secondary, #666))}blockquote>:last-child{margin-bottom:0}img{max-width:100%;height:auto;border-radius:var(--ran-markdown-image-radius, var(--ran-radius-sm, 6px))}:not(pre)>code{padding:.15em .4em;border-radius:var(--ran-radius-sm, 6px);background:var(--ran-markdown-inline-code-bg, var(--ran-gray-alpha-200, rgba(0, 0, 0, .06)));color:var(--ran-markdown-inline-code-color, inherit);font-family:var(--ran-markdown-mono-font, var(--ran-font-mono, ui-monospace, SFMono-Regular, Menlo, monospace));font-size:.875em;overflow-wrap:anywhere}.ran-md-code{margin:0 0 var(--ran-markdown-paragraph-margin, var(--ran-space-3, 12px));border:var(--ran-skin-border-width, 1px) var(--ran-skin-border-style, solid) var(--ran-markdown-code-border, var(--ran-color-border, #eaeaea));border-radius:var(--ran-markdown-code-radius, var(--ran-radius-md, 12px));background:var(--ran-markdown-code-bg, var(--ran-color-bg-muted, #fafafa));overflow:hidden;content-visibility:auto;contain-intrinsic-size:auto 120px}.ran-md-code-header{display:flex;align-items:center;justify-content:space-between;gap:var(--ran-space-2, 8px);min-height:var(--ran-markdown-code-header-height, var(--ran-size-7, 32px));padding:0 var(--ran-space-2, 8px) 0 var(--ran-space-3, 12px);border-bottom:var(--ran-skin-border-width, 1px) var(--ran-skin-border-style, solid) var(--ran-markdown-code-border, var(--ran-color-border, #eaeaea));background:var(--ran-markdown-code-header-bg, transparent)}.ran-md-code-lang{color:var(--ran-markdown-code-lang-color, var(--ran-color-text-secondary, #666));font-family:var(--ran-markdown-mono-font, var(--ran-font-mono, ui-monospace, SFMono-Regular, Menlo, monospace));font-size:var(--ran-text-label-3, 12px);line-height:1;text-transform:lowercase}.ran-md-code-actions{display:inline-flex;align-items:center;gap:var(--ran-space-1, 4px)}.ran-md-btn{display:inline-flex;align-items:center;justify-content:center;width:var(--ran-markdown-button-size, var(--ran-size-4, 24px));height:var(--ran-markdown-button-size, var(--ran-size-4, 24px));padding:0;border:none;border-radius:var(--ran-radius-sm, 6px);background:transparent;color:var(--ran-markdown-button-color, var(--ran-color-text-secondary, #666));cursor:pointer}.ran-md-btn:hover{background:var(--ran-markdown-button-hover-background, var(--ran-gray-alpha-200, rgba(0, 0, 0, .06)));color:var(--ran-markdown-button-hover-color, var(--ran-color-text, #171717))}.ran-md-btn:focus-visible{outline:var(--ran-focus-ring, 2px solid var(--ran-color-primary, #0070f3));outline-offset:1px}.ran-md-pre{margin:0;padding:var(--ran-markdown-code-padding, var(--ran-space-3, 12px) var(--ran-space-4, 16px));overflow-x:auto;background:transparent!important;color:var(--ran-markdown-code-color, var(--ran-color-text, #171717));font-family:var(--ran-markdown-mono-font, var(--ran-font-mono, ui-monospace, SFMono-Regular, Menlo, monospace));font-size:var(--ran-markdown-code-font-size, var(--ran-text-label-2, 13px));line-height:var(--ran-markdown-code-line-height, 1.6);tab-size:2}.ran-md-pre code{display:block;padding:0;background:transparent;font:inherit;white-space:pre}.ran-md-pre .line{display:inline}.ran-md-pre .line:empty:after{content:" "}.ran-md-code[data-line-numbers] .ran-md-pre code{counter-reset:line}.ran-md-code[data-line-numbers] .ran-md-pre .line:before{counter-increment:line;content:counter(line);display:inline-block;width:var(--ran-markdown-line-number-width, 2.5em);margin-right:var(--ran-space-3, 12px);color:var(--ran-markdown-line-number-color, var(--ran-color-text-disabled, #a3a3a3));text-align:right;-webkit-user-select:none;user-select:none}.ran-md-pre.shiki span{color:var(--shiki-light)}.is-dark .ran-md-pre.shiki span{color:var(--shiki-dark)}@media(prefers-color-scheme:dark){.is-system .ran-md-pre.shiki span{color:var(--shiki-dark)}}.ran-md-table-wrap{margin:0 0 var(--ran-markdown-paragraph-margin, var(--ran-space-3, 12px));border:var(--ran-skin-border-width, 1px) var(--ran-skin-border-style, solid) var(--ran-markdown-table-border, var(--ran-color-border, #eaeaea));border-radius:var(--ran-markdown-table-radius, var(--ran-radius-md, 12px));overflow-x:auto}table{width:100%;border-collapse:collapse;font-size:var(--ran-markdown-table-font-size, var(--ran-text-copy-2, 14px))}th,td{padding:var(--ran-markdown-table-cell-padding, var(--ran-space-2, 8px) var(--ran-space-3, 12px));border-bottom:var(--ran-skin-border-width, 1px) var(--ran-skin-border-style, solid) var(--ran-markdown-table-border, var(--ran-color-border, #eaeaea));text-align:left;vertical-align:top}th{background:var(--ran-markdown-table-header-bg, var(--ran-color-bg-muted, #fafafa));color:var(--ran-markdown-table-header-color, var(--ran-color-text-secondary, #666));font-weight:var(--ran-text-label-weight, 500);white-space:nowrap}tr:last-child td{border-bottom:0}.ran-md-mermaid,r-math[display=block]{display:block;max-width:100%;overflow-x:auto;margin:0 0 var(--ran-markdown-paragraph-margin, var(--ran-space-3, 12px))}.ran-md-math-block{overflow-x:auto}r-math[display=inline]{vertical-align:middle}.ran-markdown-error{margin:0;padding:var(--ran-markdown-error-padding, var(--ran-space-2, 8px) var(--ran-space-3, 12px));border-radius:var(--ran-radius-sm, 6px);background:var(--ran-markdown-error-background, var(--ran-red-100, #fff0f0));color:var(--ran-markdown-error-color, var(--ran-color-danger, #e5484d));font-family:var(--ran-font-mono, ui-monospace, monospace);font-size:var(--ran-text-label-2, 13px);white-space:pre-wrap}', E = {
javascript: "js",
js: "js",
jsx: "jsx",
typescript: "ts",
ts: "ts",
tsx: "tsx",
python: "py",
py: "py",
rust: "rs",
rs: "rs",
go: "go",
java: "java",
kotlin: "kt",
swift: "swift",
c: "c",
cpp: "cpp",
"c++": "cpp",
csharp: "cs",
cs: "cs",
"c#": "cs",
php: "php",
ruby: "rb",
rb: "rb",
bash: "sh",
sh: "sh",
shell: "sh",
zsh: "sh",
powershell: "ps1",
sql: "sql",
html: "html",
css: "css",
less: "less",
scss: "scss",
json: "json",
yaml: "yaml",
yml: "yml",
toml: "toml",
xml: "xml",
markdown: "md",
md: "md",
dockerfile: "Dockerfile",
vue: "vue",
svelte: "svelte",
dart: "dart",
lua: "lua",
r: "r",
scala: "scala",
haskell: "hs",
elixir: "ex",
graphql: "graphql",
text: "txt"
}, A = class extends O {
_shadowDom;
_wrap;
_body;
_blocks = [];
_content = null;
_pending = Promise.resolve();
_renderToken = 0;
_renderModule = null;
_themeObserver;
_lightDomObserver;
_copyResetTimer;
_error;
static get observedAttributes() {
return [
"content",
"mode",
"theme",
"caret",
"copy",
"download",
"line-numbers",
"highlight",
"inline-math",
"link-target",
"sheet"
];
}
constructor() {
super(), this._shadowDom = z(this, M);
const r = x();
this._wrap = b().class("ran-markdown").part("markdown").children(b().class("ran-markdown-body").part("body").ref(r)).build(), this._shadowDom.appendChild(this._wrap), this._body = _(r, "body");
}
get content() {
if (this._content !== null) return this._content;
const r = n(this, "content");
return r || this.textContent || "";
}
set content(r) {
const e = r == null ? "" : String(r);
this._content !== e && (this._content = e, this.render());
}
get mode() {
return n(this, "mode") || "streaming";
}
set mode(r) {
l(this, "mode", r);
}
get theme() {
return n(this, "theme") || "auto";
}
set theme(r) {
l(this, "theme", r);
}
get caret() {
return n(this, "caret");
}
set caret(r) {
l(this, "caret", r);
}
get copyable() {
return this.hasAttribute("copy");
}
set copyable(r) {
v(this, "copy", r);
}
get downloadable() {
return this.hasAttribute("download");
}
set downloadable(r) {
v(this, "download", r);
}
get lineNumbers() {
return this.hasAttribute("line-numbers");
}
set lineNumbers(r) {
v(this, "line-numbers", r);
}
get highlight() {
return this.hasAttribute("highlight") ? n(this, "highlight") : null;
}
set highlight(r) {
l(this, "highlight", r);
}
get inlineMath() {
return this.hasAttribute("inline-math");
}
set inlineMath(r) {
v(this, "inline-math", r);
}
get linkTarget() {
return n(this, "link-target") || "_blank";
}
set linkTarget(r) {
l(this, "link-target", r);
}
get sheet() {
return n(this, "sheet");
}
set sheet(r) {
l(this, "sheet", r);
}
label(r, e) {
return n(this, `label-${r}`) || e;
}
emit(r, e) {
this.dispatchEvent(new CustomEvent(r, {
detail: e,
bubbles: !0,
composed: !0
}));
}
handlerExternalCss = () => {
T(this, this._shadowDom, "sheet", null, this.sheet);
};
resolveTheme() {
const r = this.theme;
if (r === "dark" || r === "light") return r;
const e = document.documentElement, a = e.getAttribute("data-ran-theme") || e.getAttribute("theme");
return a === "dark" || e.classList.contains("dark") ? "dark" : a === "light" || e.classList.contains("light") ? "light" : "system";
}
syncTheme() {
const r = this.resolveTheme();
this._wrap.classList.toggle("is-dark", r === "dark"), this._wrap.classList.toggle("is-light", r === "light"), this._wrap.classList.toggle("is-system", r === "system");
for (const e of this._blocks) for (const a of e.themed) a.setAttribute("theme", this.theme);
}
observeTheme() {
if (this.theme !== "auto") {
this._themeObserver?.disconnect(), this._themeObserver = void 0;
return;
}
this._themeObserver || typeof MutationObserver > "u" || (this._themeObserver = new MutationObserver(() => this.syncTheme()), this._themeObserver.observe(document.documentElement, {
attributes: !0,
attributeFilter: [
"class",
"data-ran-theme",
"theme"
]
}));
}
render() {
if (typeof document > "u") return;
const r = ++this._renderToken, e = this.content;
if (this._renderModule) {
this.renderWith(this._renderModule, e, r);
return;
}
this._pending = import("./render-CSi9KMUI.js").then((a) => {
this._renderModule = a, r === this._renderToken && this.renderWith(a, e, r);
}).catch((a) => this.showError(a?.message || String(a)));
}
renderWith(r, e, a) {
if (a !== this._renderToken) return;
const i = this.mode !== "static";
try {
const o = i ? r.remend(e, { inlineKatex: this.inlineMath }) : e, s = r.parseDocument(o, {
inlineMath: this.inlineMath,
split: i
}), m = s.blocks, w = m.length - 1, g = this.highlight, f = {
copy: this.copyable,
download: this.downloadable,
lineNumbers: this.lineNumbers,
highlight: g === null ? null : r.parseThemes(g),
linkTarget: this.linkTarget,
theme: this.theme,
labels: {
copy: this.label("copy", "Copy code"),
download: this.label("download", "Download")
},
onCopy: this.copyCode,
onDownload: this.downloadCode
};
let p = 0;
for (m.forEach((d, k) => {
const h = i && k === w && d.incompleteCode, t = this._blocks[k];
if (t && t.key === d.key && t.incomplete === h) return;
const c = t ? t.el : this.createBlockElement();
c.replaceChildren(s.render(d));
const { themed: u } = r.enhance(c, {
...f,
incomplete: h
});
c.toggleAttribute("data-no-caret", h || d.hasTable), t ? (t.key = d.key, t.incomplete = h, t.themed = u) : (this._blocks.push({
key: d.key,
incomplete: h,
el: c,
themed: u
}), this._body.appendChild(c)), p += 1;
}); this._blocks.length > m.length; )
this._blocks.pop()?.el.remove(), p += 1;
this._wrap.classList.remove("has-error"), this._error?.remove(), this._error = void 0, this.syncTheme(), p > 0 && this.emit("render", {
blocks: m.length,
changed: p
});
} catch (o) {
this.showError(String(o?.message || o));
}
}
createBlockElement() {
return b().class("ran-markdown-block").part("block").build();
}
showError(r) {
this._blocks.forEach((e) => e.el.remove()), this._blocks = [], this._error || (this._error = y("pre").class("ran-markdown-error").part("error").build(), this._wrap.appendChild(this._error)), this._error.textContent = r, this._wrap.classList.add("has-error"), this.emit("error", { message: r });
}
rerender() {
this._blocks.forEach((r) => r.el.remove()), this._blocks = [], this.render();
}
copyCode = (r, e, a) => {
navigator.clipboard && navigator.clipboard.writeText(e).then(() => {
this.emit("copied", {
kind: "code",
language: a,
code: e
}), r && (r.setAttribute("name", "check"), window.clearTimeout(this._copyResetTimer), this._copyResetTimer = window.setTimeout(() => r.setAttribute("name", "copy"), 1200));
});
};
downloadCode = (r, e) => {
const a = E[e] || e || "txt", i = a === "Dockerfile" ? "Dockerfile" : `code.${a}`, o = URL.createObjectURL(new Blob([r], { type: "text/plain;charset=utf-8" })), s = document.createElement("a");
s.href = o, s.download = i, s.click(), URL.revokeObjectURL(o), this.emit("download", {
kind: "code",
language: e,
filename: i
});
};
connectedCallback() {
this.handlerExternalCss(), this.observeTheme(), typeof MutationObserver < "u" && !this._lightDomObserver && (this._lightDomObserver = new MutationObserver(() => {
this._content === null && !this.hasAttribute("content") && this.render();
}), this._lightDomObserver.observe(this, {
childList: !0,
characterData: !0,
subtree: !0
})), this.render();
}
disconnectedCallback() {
this._themeObserver?.disconnect(), this._themeObserver = void 0, this._lightDomObserver?.disconnect(), this._lightDomObserver = void 0, window.clearTimeout(this._copyResetTimer);
}
attributeChangedCallback(r, e, a) {
e !== a && (r === "sheet" ? this.handlerExternalCss() : r === "content" ? this.render() : r === "theme" ? (this.observeTheme(), this.syncTheme()) : r === "caret" || this.isConnected && this.rerender());
}
};
C("r-markdown", A);
export {
A as t
};