@noirmd/previewer 2.1.4 → 2.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1681,6 +1681,22 @@ function parseHtmlAttrs(attrsString) {
1681
1681
  }
1682
1682
 
1683
1683
  // core/parser.ts
1684
+ var VOID_ELEMENTS = /* @__PURE__ */ new Set([
1685
+ "area",
1686
+ "base",
1687
+ "br",
1688
+ "col",
1689
+ "embed",
1690
+ "hr",
1691
+ "img",
1692
+ "input",
1693
+ "link",
1694
+ "meta",
1695
+ "param",
1696
+ "source",
1697
+ "track",
1698
+ "wbr"
1699
+ ]);
1684
1700
  function parseMarkdown(markdown2) {
1685
1701
  if (!markdown2) return [];
1686
1702
  const lines = markdown2.replace(/\r\n/g, "\n").replace(/\r/g, "").split("\n");
@@ -1694,8 +1710,19 @@ function parseMarkdown(markdown2) {
1694
1710
  if (match = trimmed.match(/^(#{1,6})\s+(.+)$/)) {
1695
1711
  const level = match[1].length;
1696
1712
  const rawText = match[2];
1697
- const { text: text2, classes: classes2, id: customId } = extractAttributes(rawText);
1698
- const baseId = customId || generateId(text2.replace(/->|<-/g, ""));
1713
+ const { text: rawParsedText, classes: classes2, id: customId } = extractAttributes(rawText);
1714
+ let text2 = rawParsedText;
1715
+ let align;
1716
+ const alignCenter = text2.match(/^->\s*(.+?)\s*<-\s*$/);
1717
+ const alignRight = text2.match(/^->\s*(.+?)\s*->\s*$/);
1718
+ if (alignCenter) {
1719
+ text2 = alignCenter[1];
1720
+ align = "center";
1721
+ } else if (alignRight) {
1722
+ text2 = alignRight[1];
1723
+ align = "right";
1724
+ }
1725
+ const baseId = customId || generateId(text2);
1699
1726
  let id2 = baseId;
1700
1727
  let n = 1;
1701
1728
  while (usedIds.has(id2)) {
@@ -1703,7 +1730,7 @@ function parseMarkdown(markdown2) {
1703
1730
  id2 = `${baseId}-${n}`;
1704
1731
  }
1705
1732
  usedIds.add(id2);
1706
- result.push({ type: "header", level, text: text2, id: id2, classes: classes2 || void 0 });
1733
+ result.push({ type: "header", level, text: text2, id: id2, classes: classes2 || void 0, align });
1707
1734
  i++;
1708
1735
  continue;
1709
1736
  }
@@ -1879,29 +1906,13 @@ function parseMarkdown(markdown2) {
1879
1906
  let tagStartMatch = trimmed.match(/^<([a-zA-Z][\w-]*)/);
1880
1907
  if (tagStartMatch) {
1881
1908
  const tagName = tagStartMatch[1].toLowerCase();
1882
- const voidElements = /* @__PURE__ */ new Set([
1883
- "area",
1884
- "base",
1885
- "br",
1886
- "col",
1887
- "embed",
1888
- "hr",
1889
- "img",
1890
- "input",
1891
- "link",
1892
- "meta",
1893
- "param",
1894
- "source",
1895
- "track",
1896
- "wbr"
1897
- ]);
1898
1909
  const remainingText = lines.slice(i).join("\n");
1899
1910
  const openTagRegex = new RegExp(`^\\s*<${tagName}\\b([^>]*?)>`, "i");
1900
1911
  const openTagMatch = remainingText.match(openTagRegex);
1901
1912
  if (openTagMatch) {
1902
1913
  const fullOpenTag = openTagMatch[0];
1903
1914
  const attrs = openTagMatch[1].replace(/\s+/g, " ").trim();
1904
- const isSelfClosing = fullOpenTag.endsWith("/>") || voidElements.has(tagName);
1915
+ const isSelfClosing = fullOpenTag.endsWith("/>") || VOID_ELEMENTS.has(tagName);
1905
1916
  if (isSelfClosing) {
1906
1917
  const blockText = remainingText.substring(0, openTagMatch.index + fullOpenTag.length);
1907
1918
  const consumedLines = blockText.split("\n").length;
@@ -11078,6 +11089,7 @@ function createModal(title) {
11078
11089
  header.appendChild(titleEl);
11079
11090
  const closeBtn = document.createElement("button");
11080
11091
  closeBtn.className = "nr-modal__close";
11092
+ closeBtn.setAttribute("aria-label", "Close");
11081
11093
  closeBtn.appendChild(createIcon("close"));
11082
11094
  closeBtn.addEventListener("click", () => dialog.close());
11083
11095
  header.appendChild(closeBtn);
@@ -11298,11 +11310,89 @@ function parseInlinePart(part) {
11298
11310
  return document.createTextNode(part);
11299
11311
  }
11300
11312
 
11313
+ // vanilla/utils.ts
11314
+ var THEME_TOKENS = /* @__PURE__ */ new Set([
11315
+ "primary",
11316
+ "secondary",
11317
+ "accent",
11318
+ "neutral",
11319
+ "info",
11320
+ "success",
11321
+ "warning",
11322
+ "error"
11323
+ ]);
11324
+ function isThemeToken(color) {
11325
+ return !!color && THEME_TOKENS.has(color);
11326
+ }
11327
+ function isArbitraryColor(value) {
11328
+ if (THEME_TOKENS.has(value)) return false;
11329
+ if (/^(#|rgb|hsl|oklch|oklab|lab|lch|color\()/i.test(value)) return true;
11330
+ if (/^[a-zA-Z]+$/.test(value)) return true;
11331
+ return false;
11332
+ }
11333
+ function applyBaseProps(el, props) {
11334
+ if (props.class) {
11335
+ el.classList.add(...props.class.split(/\s+/).filter(Boolean));
11336
+ }
11337
+ if (props.style) {
11338
+ const styles = parseCssString(props.style);
11339
+ for (const [key, value] of Object.entries(styles)) {
11340
+ el.style.setProperty(key, String(value));
11341
+ }
11342
+ }
11343
+ }
11344
+ function applyFloatStyle(el, float, width) {
11345
+ if (!float) return;
11346
+ if (float === "left" || float === "right") {
11347
+ el.style.float = float;
11348
+ if (!width) el.style.maxWidth = "50%";
11349
+ el.style.marginInlineStart = float === "right" ? "1rem" : "";
11350
+ el.style.marginInlineEnd = float === "left" ? "1rem" : "";
11351
+ } else if (float === "center") {
11352
+ el.style.marginInline = "auto";
11353
+ }
11354
+ }
11355
+ function applyColor(el, color, classSuffix) {
11356
+ if (!color) return "";
11357
+ if (isThemeToken(color)) {
11358
+ return ` ${classSuffix}--${color}`;
11359
+ }
11360
+ if (isArbitraryColor(color)) {
11361
+ el.style.background = color;
11362
+ el.style.color = "white";
11363
+ }
11364
+ return "";
11365
+ }
11366
+ function openModal(dialog) {
11367
+ if (!dialog.open) {
11368
+ document.body.appendChild(dialog);
11369
+ dialog.showModal();
11370
+ dialog.addEventListener("close", () => dialog.remove(), { once: true });
11371
+ }
11372
+ }
11373
+ var IMG_RE = /!\[([^\]]*)\]\(([^)]+)\)/g;
11374
+ function applyAlignClass(el, baseClass, align) {
11375
+ if (align === "center") {
11376
+ el.classList.add(`${baseClass}--center`);
11377
+ } else if (align === "right") {
11378
+ el.classList.add(`${baseClass}--right`);
11379
+ }
11380
+ }
11381
+ function resolveIcon(value, fallback) {
11382
+ if (!value) return fallback;
11383
+ if (value === "none" || value === "off") return null;
11384
+ return value;
11385
+ }
11386
+ function parseIntProp(value, defaultValue) {
11387
+ if (!value) return defaultValue;
11388
+ const n = parseInt(value, 10);
11389
+ return Number.isNaN(n) ? defaultValue : n;
11390
+ }
11391
+
11301
11392
  // vanilla/directives/admonition.ts
11302
11393
  var admonitionDirective = ({ directiveType, props, renderSlot }) => {
11303
11394
  const el = createAdmonition(directiveType, props.title, props.icon);
11304
- if (props.class) el.classList.add(...props.class.split(/\s+/).filter(Boolean));
11305
- if (props.style) el.setAttribute("style", props.style);
11395
+ applyBaseProps(el, props);
11306
11396
  const body = el.querySelector(".nr-admonition__body");
11307
11397
  if (body) {
11308
11398
  body.appendChild(renderSlot("default"));
@@ -11318,8 +11408,7 @@ var detailsDirective = ({ props, renderSlot }) => {
11318
11408
  props.icon,
11319
11409
  props.defaultOpen === "true"
11320
11410
  );
11321
- if (props.class) el.classList.add(...props.class.split(/\s+/).filter(Boolean));
11322
- if (props.style) el.setAttribute("style", props.style);
11411
+ applyBaseProps(el, props);
11323
11412
  const body = el.querySelector(".nr-details__body");
11324
11413
  if (body) {
11325
11414
  body.appendChild(renderSlot("default"));
@@ -11332,14 +11421,17 @@ var details_default = detailsDirective;
11332
11421
  var modalDirective = ({ props, renderSlot }) => {
11333
11422
  const label = props.label || props.title || "Open";
11334
11423
  const modalTitle = props.title || "Modal";
11335
- const customClass = props.class || "";
11424
+ const align = props.align || "left";
11425
+ const iconName = resolveIcon(props.icon, "open_in_full");
11336
11426
  const wrapper = document.createElement("div");
11337
11427
  wrapper.className = "nr-modal-trigger";
11428
+ applyAlignClass(wrapper, "nr-modal-trigger", align);
11338
11429
  const btn = document.createElement("button");
11339
- btn.className = `nr-button nr-button--default`;
11340
- if (customClass) btn.classList.add(...customClass.split(/\s+/).filter(Boolean));
11341
- const icon = props.icon || "open_in_new";
11342
- if (icon) btn.appendChild(createIcon(icon));
11430
+ btn.className = "nr-button nr-button--default";
11431
+ btn.setAttribute("aria-haspopup", "dialog");
11432
+ applyColor(btn, props.color, "nr-button");
11433
+ applyBaseProps(btn, props);
11434
+ if (iconName) btn.appendChild(createIcon(iconName));
11343
11435
  btn.appendChild(document.createTextNode(label));
11344
11436
  const dialog = createModal(modalTitle);
11345
11437
  const body = dialog.querySelector(".nr-modal__body");
@@ -11349,15 +11441,7 @@ var modalDirective = ({ props, renderSlot }) => {
11349
11441
  prose.appendChild(renderSlot("default"));
11350
11442
  body.appendChild(prose);
11351
11443
  }
11352
- btn.addEventListener("click", () => {
11353
- if (!dialog.open) {
11354
- document.body.appendChild(dialog);
11355
- dialog.showModal();
11356
- dialog.addEventListener("close", () => {
11357
- dialog.remove();
11358
- }, { once: true });
11359
- }
11360
- });
11444
+ btn.addEventListener("click", () => openModal(dialog));
11361
11445
  wrapper.appendChild(btn);
11362
11446
  wrapper.appendChild(dialog);
11363
11447
  return wrapper;
@@ -11367,20 +11451,22 @@ var modal_default = modalDirective;
11367
11451
  // vanilla/directives/button.ts
11368
11452
  var buttonDirective = ({ props, renderSlot }) => {
11369
11453
  const url = props.url || props.href || "#";
11370
- const label = props.label;
11371
- const icon = props.icon || "near_me";
11454
+ const label = props.label || props.title;
11455
+ const iconName = resolveIcon(props.icon, "touch_app");
11372
11456
  const target = props.target || "_blank";
11373
- const customClass = props.class || "";
11457
+ const align = props.align || "left";
11374
11458
  const wrapper = document.createElement("div");
11375
11459
  wrapper.className = "nr-button-wrap";
11460
+ applyAlignClass(wrapper, "nr-button-wrap", align);
11376
11461
  if (label) {
11377
11462
  const a = document.createElement("a");
11378
11463
  a.href = url;
11379
11464
  a.target = target;
11380
- a.rel = "noopener noreferrer";
11465
+ if (target === "_blank") a.rel = "noopener noreferrer";
11381
11466
  a.className = "nr-button nr-button--default";
11382
- if (customClass) a.classList.add(...customClass.split(/\s+/).filter(Boolean));
11383
- a.appendChild(createIcon(icon));
11467
+ applyColor(a, props.color, "nr-button");
11468
+ applyBaseProps(a, props);
11469
+ if (iconName) a.appendChild(createIcon(iconName));
11384
11470
  a.appendChild(document.createTextNode(label));
11385
11471
  wrapper.appendChild(a);
11386
11472
  return wrapper;
@@ -11390,17 +11476,20 @@ var buttonDirective = ({ props, renderSlot }) => {
11390
11476
  if (links.length > 0) {
11391
11477
  links.forEach((link) => {
11392
11478
  link.classList.add("nr-button", "nr-button--default");
11393
- if (customClass) link.classList.add(...customClass.split(/\s+/).filter(Boolean));
11479
+ applyColor(link, props.color, "nr-button");
11480
+ if (iconName) link.prepend(createIcon(iconName));
11481
+ applyBaseProps(link, props);
11394
11482
  });
11395
11483
  wrapper.appendChild(slotContent);
11396
11484
  } else {
11397
11485
  const a = document.createElement("a");
11398
11486
  a.href = url;
11399
11487
  a.target = target;
11400
- a.rel = "noopener noreferrer";
11488
+ if (target === "_blank") a.rel = "noopener noreferrer";
11401
11489
  a.className = "nr-button nr-button--default";
11402
- if (customClass) a.classList.add(...customClass.split(/\s+/).filter(Boolean));
11403
- a.appendChild(createIcon(icon));
11490
+ applyColor(a, props.color, "nr-button");
11491
+ applyBaseProps(a, props);
11492
+ if (iconName) a.appendChild(createIcon(iconName));
11404
11493
  a.appendChild(slotContent);
11405
11494
  wrapper.appendChild(a);
11406
11495
  }
@@ -11422,12 +11511,9 @@ var cardDirective = ({
11422
11511
  const { isSingleCard } = options || {};
11423
11512
  const isModal = directiveType === "card-m";
11424
11513
  const isLink = directiveType === "card-b";
11425
- const inlineStyles = props.style ? parseCssString(props.style) : {};
11426
11514
  const card = document.createElement("div");
11427
11515
  card.className = `nr-card${isModal || isLink ? " nr-card--interactive" : ""} ${customClass}`.trim();
11428
- for (const [key, value] of Object.entries(inlineStyles)) {
11429
- card.style.setProperty(key, String(value));
11430
- }
11516
+ applyBaseProps(card, props);
11431
11517
  if (image) {
11432
11518
  const imgWrap = document.createElement("div");
11433
11519
  imgWrap.className = `nr-card__image${isSingleCard ? " nr-card__image--tall" : ""}`;
@@ -11506,13 +11592,7 @@ var cardDirective = ({
11506
11592
  prose.appendChild(renderSlot("content") || renderSlot("default"));
11507
11593
  modalBody.appendChild(prose);
11508
11594
  }
11509
- card.addEventListener("click", () => {
11510
- if (!dialog.open) {
11511
- document.body.appendChild(dialog);
11512
- dialog.showModal();
11513
- dialog.addEventListener("close", () => dialog.remove(), { once: true });
11514
- }
11515
- });
11595
+ card.addEventListener("click", () => openModal(dialog));
11516
11596
  const frag = document.createDocumentFragment();
11517
11597
  frag.appendChild(card);
11518
11598
  frag.appendChild(dialog);
@@ -11560,8 +11640,8 @@ var slideDirective = ({
11560
11640
  if (lines.length === 0) {
11561
11641
  return document.createDocumentFragment();
11562
11642
  }
11563
- const interval = parseInt(props.interval || "3000", 10);
11564
- const speed = parseInt(props.speed || "500", 10);
11643
+ const interval = parseIntProp(props.interval, 3e3);
11644
+ const speed = parseIntProp(props.speed, 500);
11565
11645
  const rawClass = props.class || "";
11566
11646
  const inlineStyle = props.style ? parseCssString(props.style) : {};
11567
11647
  const scopeClass = `sld-${++slideCounter}`;
@@ -11612,10 +11692,11 @@ var slideDirective = ({
11612
11692
  }
11613
11693
  });
11614
11694
  if (lines.length > 1) {
11615
- setInterval(() => {
11695
+ const id = setInterval(() => {
11616
11696
  current = (current + 1) % lines.length;
11617
11697
  track.style.transform = `translateY(${-current * maxH}px)`;
11618
11698
  }, interval);
11699
+ container.dataset.nrIntervalId = String(id);
11619
11700
  }
11620
11701
  return container;
11621
11702
  };
@@ -11625,8 +11706,7 @@ var slide_default = slideDirective;
11625
11706
  var keysDirective = ({ props, slots }) => {
11626
11707
  const wrap = document.createElement("div");
11627
11708
  wrap.className = "nr-keys";
11628
- if (props.class) wrap.classList.add(...props.class.split(/\s+/).filter(Boolean));
11629
- if (props.style) wrap.setAttribute("style", props.style);
11709
+ applyBaseProps(wrap, props);
11630
11710
  const sizeClass = props.size ? ` nr-kbd--${props.size}` : "";
11631
11711
  const parts = (slots.default || "").split("+").map((p) => p.trim()).filter(Boolean);
11632
11712
  parts.forEach((part, i) => {
@@ -11650,8 +11730,7 @@ var accordionCounter = 0;
11650
11730
  var accordionItemDirective = ({ props, renderSlot }) => {
11651
11731
  const item = document.createElement("div");
11652
11732
  item.className = "nr-accordion__item";
11653
- if (props.class) item.classList.add(...props.class.split(/\s+/).filter(Boolean));
11654
- if (props.style) item.setAttribute("style", props.style);
11733
+ applyBaseProps(item, props);
11655
11734
  const input = document.createElement("input");
11656
11735
  input.type = "radio";
11657
11736
  input.className = "nr-accordion__input";
@@ -11670,8 +11749,7 @@ var accordionItemDirective = ({ props, renderSlot }) => {
11670
11749
  var accordionDirective = ({ props, renderSlot }) => {
11671
11750
  const wrap = document.createElement("div");
11672
11751
  wrap.className = "nr-accordion";
11673
- if (props.class) wrap.classList.add(...props.class.split(/\s+/).filter(Boolean));
11674
- if (props.style) wrap.setAttribute("style", props.style);
11752
+ applyBaseProps(wrap, props);
11675
11753
  wrap.appendChild(renderSlot("default"));
11676
11754
  const mode = props.mode === "checkbox" ? "checkbox" : "radio";
11677
11755
  const group = `nr-acc-${++accordionCounter}`;
@@ -11684,7 +11762,6 @@ var accordionDirective = ({ props, renderSlot }) => {
11684
11762
  var accordion_default = accordionDirective;
11685
11763
 
11686
11764
  // vanilla/directives/carousel.ts
11687
- var IMG_RE = /!\[([^\]]*)\]\(([^)]+)\)/g;
11688
11765
  var carouselDirective = ({ props, slots }) => {
11689
11766
  const images = [];
11690
11767
  const raw = slots.default || "";
@@ -11700,19 +11777,9 @@ var carouselDirective = ({ props, slots }) => {
11700
11777
  wrap.className = "nr-carousel";
11701
11778
  wrap.tabIndex = 0;
11702
11779
  wrap.setAttribute("aria-label", "Image carousel");
11703
- if (props.class) wrap.classList.add(...props.class.split(/\s+/).filter(Boolean));
11704
- if (props.style) wrap.setAttribute("style", props.style);
11780
+ applyBaseProps(wrap, props);
11705
11781
  if (props.width) wrap.style.width = props.width;
11706
- if (props.float) {
11707
- if (props.float === "left" || props.float === "right") {
11708
- wrap.style.float = props.float;
11709
- if (!props.width) wrap.style.maxWidth = "50%";
11710
- wrap.style.marginInlineStart = props.float === "right" ? "1rem" : "";
11711
- wrap.style.marginInlineEnd = props.float === "left" ? "1rem" : "";
11712
- } else if (props.float === "center") {
11713
- wrap.style.marginInline = "auto";
11714
- }
11715
- }
11782
+ applyFloatStyle(wrap, props.float, props.width);
11716
11783
  const viewport = document.createElement("div");
11717
11784
  viewport.className = "nr-carousel__viewport";
11718
11785
  if (props.height) viewport.style.height = props.height;
@@ -11782,11 +11849,10 @@ var DEFAULT_LABELS = ["days", "hours", "min", "sec"];
11782
11849
  var countdownDirective = ({ props }) => {
11783
11850
  const wrap = document.createElement("div");
11784
11851
  wrap.className = "nr-countdown";
11785
- if (props.class) wrap.classList.add(...props.class.split(/\s+/).filter(Boolean));
11786
- if (props.style) wrap.setAttribute("style", props.style);
11852
+ applyBaseProps(wrap, props);
11787
11853
  const labelParts = (props.labels || "").split("|").map((s) => s.trim());
11788
11854
  const labels = DEFAULT_LABELS.map((label, i) => labelParts[i] || label);
11789
- const digits = parseInt(props.digits || "2", 10);
11855
+ const digits = parseIntProp(props.digits, 2);
11790
11856
  const targetTime = props.target ? new Date(props.target).getTime() : NaN;
11791
11857
  const hasTarget = !Number.isNaN(targetTime);
11792
11858
  const blocks = [];
@@ -11833,13 +11899,15 @@ var countdownDirective = ({ props }) => {
11833
11899
  blocks.push({ value });
11834
11900
  });
11835
11901
  render();
11836
- if (hasTarget) setInterval(render, 1e3);
11902
+ if (hasTarget) {
11903
+ const id = setInterval(render, 1e3);
11904
+ wrap.dataset.nrIntervalId = String(id);
11905
+ }
11837
11906
  return wrap;
11838
11907
  };
11839
11908
  var countdown_default = countdownDirective;
11840
11909
 
11841
11910
  // vanilla/directives/diff.ts
11842
- var IMG_RE2 = /!\[([^\]]*)\]\(([^)]+)\)/g;
11843
11911
  var diffDirective = ({ props, slots }) => {
11844
11912
  let before = (props.before || "").split("#")[0].trim();
11845
11913
  let after = (props.after || "").split("#")[0].trim();
@@ -11847,8 +11915,8 @@ var diffDirective = ({ props, slots }) => {
11847
11915
  const urls = [];
11848
11916
  const raw = slots.default || "";
11849
11917
  let m;
11850
- IMG_RE2.lastIndex = 0;
11851
- while ((m = IMG_RE2.exec(raw)) !== null) {
11918
+ IMG_RE.lastIndex = 0;
11919
+ while ((m = IMG_RE.exec(raw)) !== null) {
11852
11920
  urls.push(m[2].split("#")[0].trim());
11853
11921
  }
11854
11922
  if (!before && urls.length > 0) before = urls[0];
@@ -11861,21 +11929,11 @@ var diffDirective = ({ props, slots }) => {
11861
11929
  figure.className = "nr-diff";
11862
11930
  figure.tabIndex = 0;
11863
11931
  figure.setAttribute("aria-label", "Image comparison slider");
11864
- if (props.class) figure.classList.add(...props.class.split(/\s+/).filter(Boolean));
11865
- if (props.style) figure.setAttribute("style", props.style);
11932
+ applyBaseProps(figure, props);
11866
11933
  if (props.aspect) figure.style.aspectRatio = props.aspect;
11867
11934
  if (props.height) figure.style.height = props.height;
11868
11935
  if (props.width) figure.style.width = props.width;
11869
- if (props.float) {
11870
- if (props.float === "left" || props.float === "right") {
11871
- figure.style.float = props.float;
11872
- if (!props.width) figure.style.maxWidth = "50%";
11873
- figure.style.marginInlineStart = props.float === "right" ? "1rem" : "";
11874
- figure.style.marginInlineEnd = props.float === "left" ? "1rem" : "";
11875
- } else if (props.float === "center") {
11876
- figure.style.marginInline = "auto";
11877
- }
11878
- }
11936
+ applyFloatStyle(figure, props.float, props.width);
11879
11937
  const beforeItem = document.createElement("div");
11880
11938
  beforeItem.className = "nr-diff__item nr-diff__item--before";
11881
11939
  beforeItem.setAttribute("role", "img");
@@ -11938,8 +11996,7 @@ var diff_default = diffDirective;
11938
11996
  var hover3dDirective = ({ props, renderSlot }) => {
11939
11997
  const container = document.createElement("div");
11940
11998
  container.className = "nr-hover-3d";
11941
- if (props.class) container.classList.add(...props.class.split(/\s+/).filter(Boolean));
11942
- if (props.style) container.setAttribute("style", props.style);
11999
+ applyBaseProps(container, props);
11943
12000
  const stage = document.createElement("div");
11944
12001
  stage.className = "nr-hover-3d__stage";
11945
12002
  stage.appendChild(renderSlot("default"));
@@ -11952,14 +12009,13 @@ var hover3dDirective = ({ props, renderSlot }) => {
11952
12009
  var hover3d_default = hover3dDirective;
11953
12010
 
11954
12011
  // vanilla/directives/hovergallery.ts
11955
- var IMG_RE3 = /!\[([^\]]*)\]\(([^)]+)\)/g;
11956
12012
  var MAX_IMAGES = 10;
11957
12013
  var hovergalleryDirective = ({ props, slots }) => {
11958
12014
  const images = [];
11959
12015
  const raw = slots.default || "";
11960
12016
  let m;
11961
- IMG_RE3.lastIndex = 0;
11962
- while ((m = IMG_RE3.exec(raw)) !== null) {
12017
+ IMG_RE.lastIndex = 0;
12018
+ while ((m = IMG_RE.exec(raw)) !== null) {
11963
12019
  images.push({ src: m[2].split("#")[0].trim(), alt: m[1].trim() || "gallery image" });
11964
12020
  }
11965
12021
  if (images.length === 0) {
@@ -11968,9 +12024,8 @@ var hovergalleryDirective = ({ props, slots }) => {
11968
12024
  const count = Math.min(images.length, MAX_IMAGES);
11969
12025
  const figure = document.createElement("figure");
11970
12026
  figure.className = "nr-hover-gallery";
12027
+ applyBaseProps(figure, props);
11971
12028
  if (props.aspect) figure.style.aspectRatio = props.aspect;
11972
- if (props.class) figure.classList.add(...props.class.split(/\s+/).filter(Boolean));
11973
- if (props.style) figure.setAttribute("style", (figure.getAttribute("style") || "") + ";" + props.style);
11974
12029
  const imgEls = [];
11975
12030
  for (let i = 0; i < count; i++) {
11976
12031
  const el = document.createElement("img");
@@ -12020,28 +12075,11 @@ var hovergalleryDirective = ({ props, slots }) => {
12020
12075
  var hovergallery_default = hovergalleryDirective;
12021
12076
 
12022
12077
  // vanilla/directives/chat.ts
12023
- var CHAT_THEME_TOKENS = /* @__PURE__ */ new Set([
12024
- "primary",
12025
- "secondary",
12026
- "accent",
12027
- "neutral",
12028
- "info",
12029
- "success",
12030
- "warning",
12031
- "error"
12032
- ]);
12033
- function isArbitraryColor(value) {
12034
- if (CHAT_THEME_TOKENS.has(value)) return false;
12035
- if (/^(#|rgb|hsl|oklch|oklab|lab|lch|color\()/i.test(value)) return true;
12036
- if (/^[a-zA-Z]+$/.test(value)) return true;
12037
- return false;
12038
- }
12039
12078
  var chatItemDirective = ({ props, renderSlot }) => {
12040
12079
  const side = props.side === "end" ? "end" : "start";
12041
12080
  const wrap = document.createElement("div");
12042
12081
  wrap.className = `nr-chat nr-chat--${side}`;
12043
- if (props.class) wrap.classList.add(...props.class.split(/\s+/).filter(Boolean));
12044
- if (props.style) wrap.setAttribute("style", props.style);
12082
+ applyBaseProps(wrap, props);
12045
12083
  const header = document.createElement("div");
12046
12084
  header.className = "nr-chat__header";
12047
12085
  if (props.name) {
@@ -12066,14 +12104,8 @@ var chatItemDirective = ({ props, renderSlot }) => {
12066
12104
  avatar.appendChild(img);
12067
12105
  wrap.appendChild(avatar);
12068
12106
  }
12069
- const isThemeToken = CHAT_THEME_TOKENS.has(props.color || "");
12070
- const colorClass = isThemeToken ? ` nr-chat__bubble--${props.color}` : "";
12071
12107
  const bubble = document.createElement("div");
12072
- bubble.className = `nr-chat__bubble${colorClass}`;
12073
- if (props.color && isArbitraryColor(props.color) && !isThemeToken) {
12074
- bubble.style.background = props.color;
12075
- bubble.style.color = "white";
12076
- }
12108
+ bubble.className = `nr-chat__bubble${applyColor(bubble, props.color, "nr-chat__bubble")}`;
12077
12109
  bubble.appendChild(renderSlot("default"));
12078
12110
  wrap.appendChild(bubble);
12079
12111
  if (props.footer) {
@@ -12087,8 +12119,7 @@ var chatItemDirective = ({ props, renderSlot }) => {
12087
12119
  var chatDirective = ({ props, renderSlot }) => {
12088
12120
  const wrap = document.createElement("div");
12089
12121
  wrap.className = "nr-chat";
12090
- if (props.class) wrap.classList.add(...props.class.split(/\s+/).filter(Boolean));
12091
- if (props.style) wrap.setAttribute("style", props.style);
12122
+ applyBaseProps(wrap, props);
12092
12123
  wrap.appendChild(renderSlot("default"));
12093
12124
  return wrap;
12094
12125
  };
@@ -12124,8 +12155,7 @@ function bindEventProp(el, eventProp) {
12124
12155
  var richlistItemDirective = ({ props, renderSlot }) => {
12125
12156
  const li = document.createElement("li");
12126
12157
  li.className = "nr-richlist__item";
12127
- if (props.class) li.classList.add(...props.class.split(/\s+/).filter(Boolean));
12128
- if (props.style) li.setAttribute("style", props.style);
12158
+ applyBaseProps(li, props);
12129
12159
  if (props.image) {
12130
12160
  const thumb = document.createElement("div");
12131
12161
  thumb.className = "nr-richlist__thumb";
@@ -12187,36 +12217,20 @@ var richlistItemDirective = ({ props, renderSlot }) => {
12187
12217
  var richlistDirective = ({ props, renderSlot }) => {
12188
12218
  const ul = document.createElement("ul");
12189
12219
  ul.className = "nr-richlist";
12190
- if (props.class) ul.classList.add(...props.class.split(/\s+/).filter(Boolean));
12191
- if (props.style) ul.setAttribute("style", props.style);
12220
+ applyBaseProps(ul, props);
12192
12221
  ul.appendChild(renderSlot("default"));
12193
12222
  return ul;
12194
12223
  };
12195
12224
  var richlist_default = richlistDirective;
12196
12225
 
12197
12226
  // vanilla/directives/stat.ts
12198
- var STAT_THEME_TOKENS = /* @__PURE__ */ new Set([
12199
- "primary",
12200
- "secondary",
12201
- "info",
12202
- "success",
12203
- "warning",
12204
- "error"
12205
- ]);
12206
- function isArbitraryColor2(value) {
12207
- if (STAT_THEME_TOKENS.has(value)) return false;
12208
- if (/^(#|rgb|hsl|oklch|oklab|lab|lch|color\()/i.test(value)) return true;
12209
- if (/^[a-zA-Z]+$/.test(value)) return true;
12210
- return false;
12211
- }
12212
12227
  var statDirective = ({ props }) => {
12213
- const isThemeToken = STAT_THEME_TOKENS.has(props.color || "");
12214
- const colorClass = isThemeToken ? ` nr-stat--${props.color}` : "";
12228
+ const statIsThemeToken = isThemeToken(props.color);
12229
+ const colorClass = statIsThemeToken ? ` nr-stat--${props.color}` : "";
12215
12230
  const stat = document.createElement("div");
12216
12231
  stat.className = `nr-stat${colorClass}`;
12217
- if (props.class) stat.classList.add(...props.class.split(/\s+/).filter(Boolean));
12218
- if (props.style) stat.setAttribute("style", props.style);
12219
- const useInlineColor = props.color && isArbitraryColor2(props.color) && !isThemeToken;
12232
+ applyBaseProps(stat, props);
12233
+ const useInlineColor = props.color && isArbitraryColor(props.color) && !statIsThemeToken;
12220
12234
  if (props.icon) {
12221
12235
  const figure = document.createElement("div");
12222
12236
  figure.className = "nr-stat__figure";
@@ -12309,9 +12323,12 @@ function renderHtmlString(html) {
12309
12323
  processedContent = processedContent.replace(
12310
12324
  /<style(?:\s+[^>]*)?>([\s\S]*?)<\/style>/gi,
12311
12325
  (_match, cssContent) => {
12326
+ const trimmed = cssContent.trim();
12327
+ const existing = document.head.querySelector("style[data-nr-global]");
12328
+ if (existing && existing.textContent === trimmed) return "";
12312
12329
  const styleEl = document.createElement("style");
12313
12330
  styleEl.setAttribute("data-nr-global", "");
12314
- styleEl.textContent = cssContent;
12331
+ styleEl.textContent = trimmed;
12315
12332
  document.head.appendChild(styleEl);
12316
12333
  return "";
12317
12334
  }
@@ -12381,19 +12398,14 @@ function renderElement(element, ctx, allElements) {
12381
12398
  switch (element.type) {
12382
12399
  case "header": {
12383
12400
  const tag = `h${element.level}`;
12384
- let text = element.text;
12385
- const alignCenter = text.match(/^->\s*(.+?)\s*<-\s*$/);
12386
- const alignRight = text.match(/^->\s*(.+?)\s*->\s*$/);
12387
- if (alignCenter) text = alignCenter[1];
12388
- else if (alignRight) text = alignRight[1];
12389
12401
  const h = document.createElement(tag);
12390
12402
  h.id = element.id;
12391
12403
  let cls = `md-h${element.level}`;
12392
- if (alignCenter) cls += " text-center";
12393
- if (alignRight) cls += " text-right";
12404
+ if (element.align === "center") cls += " text-center";
12405
+ else if (element.align === "right") cls += " text-right";
12394
12406
  if (element.classes) cls += ` ${element.classes}`;
12395
12407
  h.className = cls;
12396
- h.appendChild(renderInline(text));
12408
+ h.appendChild(renderInline(element.text));
12397
12409
  return h;
12398
12410
  }
12399
12411
  case "paragraph": {