@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/vanilla.cjs CHANGED
@@ -1672,6 +1672,22 @@ function parseHtmlAttrs(attrsString) {
1672
1672
  }
1673
1673
 
1674
1674
  // core/parser.ts
1675
+ var VOID_ELEMENTS = /* @__PURE__ */ new Set([
1676
+ "area",
1677
+ "base",
1678
+ "br",
1679
+ "col",
1680
+ "embed",
1681
+ "hr",
1682
+ "img",
1683
+ "input",
1684
+ "link",
1685
+ "meta",
1686
+ "param",
1687
+ "source",
1688
+ "track",
1689
+ "wbr"
1690
+ ]);
1675
1691
  function parseMarkdown(markdown2) {
1676
1692
  if (!markdown2) return [];
1677
1693
  const lines = markdown2.replace(/\r\n/g, "\n").replace(/\r/g, "").split("\n");
@@ -1685,8 +1701,19 @@ function parseMarkdown(markdown2) {
1685
1701
  if (match = trimmed.match(/^(#{1,6})\s+(.+)$/)) {
1686
1702
  const level = match[1].length;
1687
1703
  const rawText = match[2];
1688
- const { text: text2, classes: classes2, id: customId } = extractAttributes(rawText);
1689
- const baseId = customId || generateId(text2.replace(/->|<-/g, ""));
1704
+ const { text: rawParsedText, classes: classes2, id: customId } = extractAttributes(rawText);
1705
+ let text2 = rawParsedText;
1706
+ let align;
1707
+ const alignCenter = text2.match(/^->\s*(.+?)\s*<-\s*$/);
1708
+ const alignRight = text2.match(/^->\s*(.+?)\s*->\s*$/);
1709
+ if (alignCenter) {
1710
+ text2 = alignCenter[1];
1711
+ align = "center";
1712
+ } else if (alignRight) {
1713
+ text2 = alignRight[1];
1714
+ align = "right";
1715
+ }
1716
+ const baseId = customId || generateId(text2);
1690
1717
  let id2 = baseId;
1691
1718
  let n = 1;
1692
1719
  while (usedIds.has(id2)) {
@@ -1694,7 +1721,7 @@ function parseMarkdown(markdown2) {
1694
1721
  id2 = `${baseId}-${n}`;
1695
1722
  }
1696
1723
  usedIds.add(id2);
1697
- result.push({ type: "header", level, text: text2, id: id2, classes: classes2 || void 0 });
1724
+ result.push({ type: "header", level, text: text2, id: id2, classes: classes2 || void 0, align });
1698
1725
  i++;
1699
1726
  continue;
1700
1727
  }
@@ -1870,29 +1897,13 @@ function parseMarkdown(markdown2) {
1870
1897
  let tagStartMatch = trimmed.match(/^<([a-zA-Z][\w-]*)/);
1871
1898
  if (tagStartMatch) {
1872
1899
  const tagName = tagStartMatch[1].toLowerCase();
1873
- const voidElements = /* @__PURE__ */ new Set([
1874
- "area",
1875
- "base",
1876
- "br",
1877
- "col",
1878
- "embed",
1879
- "hr",
1880
- "img",
1881
- "input",
1882
- "link",
1883
- "meta",
1884
- "param",
1885
- "source",
1886
- "track",
1887
- "wbr"
1888
- ]);
1889
1900
  const remainingText = lines.slice(i).join("\n");
1890
1901
  const openTagRegex = new RegExp(`^\\s*<${tagName}\\b([^>]*?)>`, "i");
1891
1902
  const openTagMatch = remainingText.match(openTagRegex);
1892
1903
  if (openTagMatch) {
1893
1904
  const fullOpenTag = openTagMatch[0];
1894
1905
  const attrs = openTagMatch[1].replace(/\s+/g, " ").trim();
1895
- const isSelfClosing = fullOpenTag.endsWith("/>") || voidElements.has(tagName);
1906
+ const isSelfClosing = fullOpenTag.endsWith("/>") || VOID_ELEMENTS.has(tagName);
1896
1907
  if (isSelfClosing) {
1897
1908
  const blockText = remainingText.substring(0, openTagMatch.index + fullOpenTag.length);
1898
1909
  const consumedLines = blockText.split("\n").length;
@@ -11066,6 +11077,7 @@ function createModal(title) {
11066
11077
  header.appendChild(titleEl);
11067
11078
  const closeBtn = document.createElement("button");
11068
11079
  closeBtn.className = "nr-modal__close";
11080
+ closeBtn.setAttribute("aria-label", "Close");
11069
11081
  closeBtn.appendChild(createIcon("close"));
11070
11082
  closeBtn.addEventListener("click", () => dialog.close());
11071
11083
  header.appendChild(closeBtn);
@@ -11286,11 +11298,89 @@ function parseInlinePart(part) {
11286
11298
  return document.createTextNode(part);
11287
11299
  }
11288
11300
 
11301
+ // vanilla/utils.ts
11302
+ var THEME_TOKENS = /* @__PURE__ */ new Set([
11303
+ "primary",
11304
+ "secondary",
11305
+ "accent",
11306
+ "neutral",
11307
+ "info",
11308
+ "success",
11309
+ "warning",
11310
+ "error"
11311
+ ]);
11312
+ function isThemeToken(color) {
11313
+ return !!color && THEME_TOKENS.has(color);
11314
+ }
11315
+ function isArbitraryColor(value) {
11316
+ if (THEME_TOKENS.has(value)) return false;
11317
+ if (/^(#|rgb|hsl|oklch|oklab|lab|lch|color\()/i.test(value)) return true;
11318
+ if (/^[a-zA-Z]+$/.test(value)) return true;
11319
+ return false;
11320
+ }
11321
+ function applyBaseProps(el, props) {
11322
+ if (props.class) {
11323
+ el.classList.add(...props.class.split(/\s+/).filter(Boolean));
11324
+ }
11325
+ if (props.style) {
11326
+ const styles = parseCssString(props.style);
11327
+ for (const [key, value] of Object.entries(styles)) {
11328
+ el.style.setProperty(key, String(value));
11329
+ }
11330
+ }
11331
+ }
11332
+ function applyFloatStyle(el, float, width) {
11333
+ if (!float) return;
11334
+ if (float === "left" || float === "right") {
11335
+ el.style.float = float;
11336
+ if (!width) el.style.maxWidth = "50%";
11337
+ el.style.marginInlineStart = float === "right" ? "1rem" : "";
11338
+ el.style.marginInlineEnd = float === "left" ? "1rem" : "";
11339
+ } else if (float === "center") {
11340
+ el.style.marginInline = "auto";
11341
+ }
11342
+ }
11343
+ function applyColor(el, color, classSuffix) {
11344
+ if (!color) return "";
11345
+ if (isThemeToken(color)) {
11346
+ return ` ${classSuffix}--${color}`;
11347
+ }
11348
+ if (isArbitraryColor(color)) {
11349
+ el.style.background = color;
11350
+ el.style.color = "white";
11351
+ }
11352
+ return "";
11353
+ }
11354
+ function openModal(dialog) {
11355
+ if (!dialog.open) {
11356
+ document.body.appendChild(dialog);
11357
+ dialog.showModal();
11358
+ dialog.addEventListener("close", () => dialog.remove(), { once: true });
11359
+ }
11360
+ }
11361
+ var IMG_RE = /!\[([^\]]*)\]\(([^)]+)\)/g;
11362
+ function applyAlignClass(el, baseClass, align) {
11363
+ if (align === "center") {
11364
+ el.classList.add(`${baseClass}--center`);
11365
+ } else if (align === "right") {
11366
+ el.classList.add(`${baseClass}--right`);
11367
+ }
11368
+ }
11369
+ function resolveIcon(value, fallback) {
11370
+ if (!value) return fallback;
11371
+ if (value === "none" || value === "off") return null;
11372
+ return value;
11373
+ }
11374
+ function parseIntProp(value, defaultValue) {
11375
+ if (!value) return defaultValue;
11376
+ const n = parseInt(value, 10);
11377
+ return Number.isNaN(n) ? defaultValue : n;
11378
+ }
11379
+
11289
11380
  // vanilla/directives/admonition.ts
11290
11381
  var admonitionDirective = ({ directiveType, props, renderSlot }) => {
11291
11382
  const el = createAdmonition(directiveType, props.title, props.icon);
11292
- if (props.class) el.classList.add(...props.class.split(/\s+/).filter(Boolean));
11293
- if (props.style) el.setAttribute("style", props.style);
11383
+ applyBaseProps(el, props);
11294
11384
  const body = el.querySelector(".nr-admonition__body");
11295
11385
  if (body) {
11296
11386
  body.appendChild(renderSlot("default"));
@@ -11306,8 +11396,7 @@ var detailsDirective = ({ props, renderSlot }) => {
11306
11396
  props.icon,
11307
11397
  props.defaultOpen === "true"
11308
11398
  );
11309
- if (props.class) el.classList.add(...props.class.split(/\s+/).filter(Boolean));
11310
- if (props.style) el.setAttribute("style", props.style);
11399
+ applyBaseProps(el, props);
11311
11400
  const body = el.querySelector(".nr-details__body");
11312
11401
  if (body) {
11313
11402
  body.appendChild(renderSlot("default"));
@@ -11320,14 +11409,17 @@ var details_default = detailsDirective;
11320
11409
  var modalDirective = ({ props, renderSlot }) => {
11321
11410
  const label = props.label || props.title || "Open";
11322
11411
  const modalTitle = props.title || "Modal";
11323
- const customClass = props.class || "";
11412
+ const align = props.align || "left";
11413
+ const iconName = resolveIcon(props.icon, "open_in_full");
11324
11414
  const wrapper = document.createElement("div");
11325
11415
  wrapper.className = "nr-modal-trigger";
11416
+ applyAlignClass(wrapper, "nr-modal-trigger", align);
11326
11417
  const btn = document.createElement("button");
11327
- btn.className = `nr-button nr-button--default`;
11328
- if (customClass) btn.classList.add(...customClass.split(/\s+/).filter(Boolean));
11329
- const icon = props.icon || "open_in_new";
11330
- if (icon) btn.appendChild(createIcon(icon));
11418
+ btn.className = "nr-button nr-button--default";
11419
+ btn.setAttribute("aria-haspopup", "dialog");
11420
+ applyColor(btn, props.color, "nr-button");
11421
+ applyBaseProps(btn, props);
11422
+ if (iconName) btn.appendChild(createIcon(iconName));
11331
11423
  btn.appendChild(document.createTextNode(label));
11332
11424
  const dialog = createModal(modalTitle);
11333
11425
  const body = dialog.querySelector(".nr-modal__body");
@@ -11337,15 +11429,7 @@ var modalDirective = ({ props, renderSlot }) => {
11337
11429
  prose.appendChild(renderSlot("default"));
11338
11430
  body.appendChild(prose);
11339
11431
  }
11340
- btn.addEventListener("click", () => {
11341
- if (!dialog.open) {
11342
- document.body.appendChild(dialog);
11343
- dialog.showModal();
11344
- dialog.addEventListener("close", () => {
11345
- dialog.remove();
11346
- }, { once: true });
11347
- }
11348
- });
11432
+ btn.addEventListener("click", () => openModal(dialog));
11349
11433
  wrapper.appendChild(btn);
11350
11434
  wrapper.appendChild(dialog);
11351
11435
  return wrapper;
@@ -11355,20 +11439,22 @@ var modal_default = modalDirective;
11355
11439
  // vanilla/directives/button.ts
11356
11440
  var buttonDirective = ({ props, renderSlot }) => {
11357
11441
  const url = props.url || props.href || "#";
11358
- const label = props.label;
11359
- const icon = props.icon || "near_me";
11442
+ const label = props.label || props.title;
11443
+ const iconName = resolveIcon(props.icon, "touch_app");
11360
11444
  const target = props.target || "_blank";
11361
- const customClass = props.class || "";
11445
+ const align = props.align || "left";
11362
11446
  const wrapper = document.createElement("div");
11363
11447
  wrapper.className = "nr-button-wrap";
11448
+ applyAlignClass(wrapper, "nr-button-wrap", align);
11364
11449
  if (label) {
11365
11450
  const a = document.createElement("a");
11366
11451
  a.href = url;
11367
11452
  a.target = target;
11368
- a.rel = "noopener noreferrer";
11453
+ if (target === "_blank") a.rel = "noopener noreferrer";
11369
11454
  a.className = "nr-button nr-button--default";
11370
- if (customClass) a.classList.add(...customClass.split(/\s+/).filter(Boolean));
11371
- a.appendChild(createIcon(icon));
11455
+ applyColor(a, props.color, "nr-button");
11456
+ applyBaseProps(a, props);
11457
+ if (iconName) a.appendChild(createIcon(iconName));
11372
11458
  a.appendChild(document.createTextNode(label));
11373
11459
  wrapper.appendChild(a);
11374
11460
  return wrapper;
@@ -11378,17 +11464,20 @@ var buttonDirective = ({ props, renderSlot }) => {
11378
11464
  if (links.length > 0) {
11379
11465
  links.forEach((link) => {
11380
11466
  link.classList.add("nr-button", "nr-button--default");
11381
- if (customClass) link.classList.add(...customClass.split(/\s+/).filter(Boolean));
11467
+ applyColor(link, props.color, "nr-button");
11468
+ if (iconName) link.prepend(createIcon(iconName));
11469
+ applyBaseProps(link, props);
11382
11470
  });
11383
11471
  wrapper.appendChild(slotContent);
11384
11472
  } else {
11385
11473
  const a = document.createElement("a");
11386
11474
  a.href = url;
11387
11475
  a.target = target;
11388
- a.rel = "noopener noreferrer";
11476
+ if (target === "_blank") a.rel = "noopener noreferrer";
11389
11477
  a.className = "nr-button nr-button--default";
11390
- if (customClass) a.classList.add(...customClass.split(/\s+/).filter(Boolean));
11391
- a.appendChild(createIcon(icon));
11478
+ applyColor(a, props.color, "nr-button");
11479
+ applyBaseProps(a, props);
11480
+ if (iconName) a.appendChild(createIcon(iconName));
11392
11481
  a.appendChild(slotContent);
11393
11482
  wrapper.appendChild(a);
11394
11483
  }
@@ -11410,12 +11499,9 @@ var cardDirective = ({
11410
11499
  const { isSingleCard } = options || {};
11411
11500
  const isModal = directiveType === "card-m";
11412
11501
  const isLink = directiveType === "card-b";
11413
- const inlineStyles = props.style ? parseCssString(props.style) : {};
11414
11502
  const card = document.createElement("div");
11415
11503
  card.className = `nr-card${isModal || isLink ? " nr-card--interactive" : ""} ${customClass}`.trim();
11416
- for (const [key, value] of Object.entries(inlineStyles)) {
11417
- card.style.setProperty(key, String(value));
11418
- }
11504
+ applyBaseProps(card, props);
11419
11505
  if (image) {
11420
11506
  const imgWrap = document.createElement("div");
11421
11507
  imgWrap.className = `nr-card__image${isSingleCard ? " nr-card__image--tall" : ""}`;
@@ -11494,13 +11580,7 @@ var cardDirective = ({
11494
11580
  prose.appendChild(renderSlot("content") || renderSlot("default"));
11495
11581
  modalBody.appendChild(prose);
11496
11582
  }
11497
- card.addEventListener("click", () => {
11498
- if (!dialog.open) {
11499
- document.body.appendChild(dialog);
11500
- dialog.showModal();
11501
- dialog.addEventListener("close", () => dialog.remove(), { once: true });
11502
- }
11503
- });
11583
+ card.addEventListener("click", () => openModal(dialog));
11504
11584
  const frag = document.createDocumentFragment();
11505
11585
  frag.appendChild(card);
11506
11586
  frag.appendChild(dialog);
@@ -11548,8 +11628,8 @@ var slideDirective = ({
11548
11628
  if (lines.length === 0) {
11549
11629
  return document.createDocumentFragment();
11550
11630
  }
11551
- const interval = parseInt(props.interval || "3000", 10);
11552
- const speed = parseInt(props.speed || "500", 10);
11631
+ const interval = parseIntProp(props.interval, 3e3);
11632
+ const speed = parseIntProp(props.speed, 500);
11553
11633
  const rawClass = props.class || "";
11554
11634
  const inlineStyle = props.style ? parseCssString(props.style) : {};
11555
11635
  const scopeClass = `sld-${++slideCounter}`;
@@ -11600,10 +11680,11 @@ var slideDirective = ({
11600
11680
  }
11601
11681
  });
11602
11682
  if (lines.length > 1) {
11603
- setInterval(() => {
11683
+ const id = setInterval(() => {
11604
11684
  current = (current + 1) % lines.length;
11605
11685
  track.style.transform = `translateY(${-current * maxH}px)`;
11606
11686
  }, interval);
11687
+ container.dataset.nrIntervalId = String(id);
11607
11688
  }
11608
11689
  return container;
11609
11690
  };
@@ -11613,8 +11694,7 @@ var slide_default = slideDirective;
11613
11694
  var keysDirective = ({ props, slots }) => {
11614
11695
  const wrap = document.createElement("div");
11615
11696
  wrap.className = "nr-keys";
11616
- if (props.class) wrap.classList.add(...props.class.split(/\s+/).filter(Boolean));
11617
- if (props.style) wrap.setAttribute("style", props.style);
11697
+ applyBaseProps(wrap, props);
11618
11698
  const sizeClass = props.size ? ` nr-kbd--${props.size}` : "";
11619
11699
  const parts = (slots.default || "").split("+").map((p) => p.trim()).filter(Boolean);
11620
11700
  parts.forEach((part, i) => {
@@ -11638,8 +11718,7 @@ var accordionCounter = 0;
11638
11718
  var accordionItemDirective = ({ props, renderSlot }) => {
11639
11719
  const item = document.createElement("div");
11640
11720
  item.className = "nr-accordion__item";
11641
- if (props.class) item.classList.add(...props.class.split(/\s+/).filter(Boolean));
11642
- if (props.style) item.setAttribute("style", props.style);
11721
+ applyBaseProps(item, props);
11643
11722
  const input = document.createElement("input");
11644
11723
  input.type = "radio";
11645
11724
  input.className = "nr-accordion__input";
@@ -11658,8 +11737,7 @@ var accordionItemDirective = ({ props, renderSlot }) => {
11658
11737
  var accordionDirective = ({ props, renderSlot }) => {
11659
11738
  const wrap = document.createElement("div");
11660
11739
  wrap.className = "nr-accordion";
11661
- if (props.class) wrap.classList.add(...props.class.split(/\s+/).filter(Boolean));
11662
- if (props.style) wrap.setAttribute("style", props.style);
11740
+ applyBaseProps(wrap, props);
11663
11741
  wrap.appendChild(renderSlot("default"));
11664
11742
  const mode = props.mode === "checkbox" ? "checkbox" : "radio";
11665
11743
  const group = `nr-acc-${++accordionCounter}`;
@@ -11672,7 +11750,6 @@ var accordionDirective = ({ props, renderSlot }) => {
11672
11750
  var accordion_default = accordionDirective;
11673
11751
 
11674
11752
  // vanilla/directives/carousel.ts
11675
- var IMG_RE = /!\[([^\]]*)\]\(([^)]+)\)/g;
11676
11753
  var carouselDirective = ({ props, slots }) => {
11677
11754
  const images = [];
11678
11755
  const raw = slots.default || "";
@@ -11688,19 +11765,9 @@ var carouselDirective = ({ props, slots }) => {
11688
11765
  wrap.className = "nr-carousel";
11689
11766
  wrap.tabIndex = 0;
11690
11767
  wrap.setAttribute("aria-label", "Image carousel");
11691
- if (props.class) wrap.classList.add(...props.class.split(/\s+/).filter(Boolean));
11692
- if (props.style) wrap.setAttribute("style", props.style);
11768
+ applyBaseProps(wrap, props);
11693
11769
  if (props.width) wrap.style.width = props.width;
11694
- if (props.float) {
11695
- if (props.float === "left" || props.float === "right") {
11696
- wrap.style.float = props.float;
11697
- if (!props.width) wrap.style.maxWidth = "50%";
11698
- wrap.style.marginInlineStart = props.float === "right" ? "1rem" : "";
11699
- wrap.style.marginInlineEnd = props.float === "left" ? "1rem" : "";
11700
- } else if (props.float === "center") {
11701
- wrap.style.marginInline = "auto";
11702
- }
11703
- }
11770
+ applyFloatStyle(wrap, props.float, props.width);
11704
11771
  const viewport = document.createElement("div");
11705
11772
  viewport.className = "nr-carousel__viewport";
11706
11773
  if (props.height) viewport.style.height = props.height;
@@ -11770,11 +11837,10 @@ var DEFAULT_LABELS = ["days", "hours", "min", "sec"];
11770
11837
  var countdownDirective = ({ props }) => {
11771
11838
  const wrap = document.createElement("div");
11772
11839
  wrap.className = "nr-countdown";
11773
- if (props.class) wrap.classList.add(...props.class.split(/\s+/).filter(Boolean));
11774
- if (props.style) wrap.setAttribute("style", props.style);
11840
+ applyBaseProps(wrap, props);
11775
11841
  const labelParts = (props.labels || "").split("|").map((s) => s.trim());
11776
11842
  const labels = DEFAULT_LABELS.map((label, i) => labelParts[i] || label);
11777
- const digits = parseInt(props.digits || "2", 10);
11843
+ const digits = parseIntProp(props.digits, 2);
11778
11844
  const targetTime = props.target ? new Date(props.target).getTime() : NaN;
11779
11845
  const hasTarget = !Number.isNaN(targetTime);
11780
11846
  const blocks = [];
@@ -11821,13 +11887,15 @@ var countdownDirective = ({ props }) => {
11821
11887
  blocks.push({ value });
11822
11888
  });
11823
11889
  render();
11824
- if (hasTarget) setInterval(render, 1e3);
11890
+ if (hasTarget) {
11891
+ const id = setInterval(render, 1e3);
11892
+ wrap.dataset.nrIntervalId = String(id);
11893
+ }
11825
11894
  return wrap;
11826
11895
  };
11827
11896
  var countdown_default = countdownDirective;
11828
11897
 
11829
11898
  // vanilla/directives/diff.ts
11830
- var IMG_RE2 = /!\[([^\]]*)\]\(([^)]+)\)/g;
11831
11899
  var diffDirective = ({ props, slots }) => {
11832
11900
  let before = (props.before || "").split("#")[0].trim();
11833
11901
  let after = (props.after || "").split("#")[0].trim();
@@ -11835,8 +11903,8 @@ var diffDirective = ({ props, slots }) => {
11835
11903
  const urls = [];
11836
11904
  const raw = slots.default || "";
11837
11905
  let m;
11838
- IMG_RE2.lastIndex = 0;
11839
- while ((m = IMG_RE2.exec(raw)) !== null) {
11906
+ IMG_RE.lastIndex = 0;
11907
+ while ((m = IMG_RE.exec(raw)) !== null) {
11840
11908
  urls.push(m[2].split("#")[0].trim());
11841
11909
  }
11842
11910
  if (!before && urls.length > 0) before = urls[0];
@@ -11849,21 +11917,11 @@ var diffDirective = ({ props, slots }) => {
11849
11917
  figure.className = "nr-diff";
11850
11918
  figure.tabIndex = 0;
11851
11919
  figure.setAttribute("aria-label", "Image comparison slider");
11852
- if (props.class) figure.classList.add(...props.class.split(/\s+/).filter(Boolean));
11853
- if (props.style) figure.setAttribute("style", props.style);
11920
+ applyBaseProps(figure, props);
11854
11921
  if (props.aspect) figure.style.aspectRatio = props.aspect;
11855
11922
  if (props.height) figure.style.height = props.height;
11856
11923
  if (props.width) figure.style.width = props.width;
11857
- if (props.float) {
11858
- if (props.float === "left" || props.float === "right") {
11859
- figure.style.float = props.float;
11860
- if (!props.width) figure.style.maxWidth = "50%";
11861
- figure.style.marginInlineStart = props.float === "right" ? "1rem" : "";
11862
- figure.style.marginInlineEnd = props.float === "left" ? "1rem" : "";
11863
- } else if (props.float === "center") {
11864
- figure.style.marginInline = "auto";
11865
- }
11866
- }
11924
+ applyFloatStyle(figure, props.float, props.width);
11867
11925
  const beforeItem = document.createElement("div");
11868
11926
  beforeItem.className = "nr-diff__item nr-diff__item--before";
11869
11927
  beforeItem.setAttribute("role", "img");
@@ -11926,8 +11984,7 @@ var diff_default = diffDirective;
11926
11984
  var hover3dDirective = ({ props, renderSlot }) => {
11927
11985
  const container = document.createElement("div");
11928
11986
  container.className = "nr-hover-3d";
11929
- if (props.class) container.classList.add(...props.class.split(/\s+/).filter(Boolean));
11930
- if (props.style) container.setAttribute("style", props.style);
11987
+ applyBaseProps(container, props);
11931
11988
  const stage = document.createElement("div");
11932
11989
  stage.className = "nr-hover-3d__stage";
11933
11990
  stage.appendChild(renderSlot("default"));
@@ -11940,14 +11997,13 @@ var hover3dDirective = ({ props, renderSlot }) => {
11940
11997
  var hover3d_default = hover3dDirective;
11941
11998
 
11942
11999
  // vanilla/directives/hovergallery.ts
11943
- var IMG_RE3 = /!\[([^\]]*)\]\(([^)]+)\)/g;
11944
12000
  var MAX_IMAGES = 10;
11945
12001
  var hovergalleryDirective = ({ props, slots }) => {
11946
12002
  const images = [];
11947
12003
  const raw = slots.default || "";
11948
12004
  let m;
11949
- IMG_RE3.lastIndex = 0;
11950
- while ((m = IMG_RE3.exec(raw)) !== null) {
12005
+ IMG_RE.lastIndex = 0;
12006
+ while ((m = IMG_RE.exec(raw)) !== null) {
11951
12007
  images.push({ src: m[2].split("#")[0].trim(), alt: m[1].trim() || "gallery image" });
11952
12008
  }
11953
12009
  if (images.length === 0) {
@@ -11956,9 +12012,8 @@ var hovergalleryDirective = ({ props, slots }) => {
11956
12012
  const count = Math.min(images.length, MAX_IMAGES);
11957
12013
  const figure = document.createElement("figure");
11958
12014
  figure.className = "nr-hover-gallery";
12015
+ applyBaseProps(figure, props);
11959
12016
  if (props.aspect) figure.style.aspectRatio = props.aspect;
11960
- if (props.class) figure.classList.add(...props.class.split(/\s+/).filter(Boolean));
11961
- if (props.style) figure.setAttribute("style", (figure.getAttribute("style") || "") + ";" + props.style);
11962
12017
  const imgEls = [];
11963
12018
  for (let i = 0; i < count; i++) {
11964
12019
  const el = document.createElement("img");
@@ -12008,28 +12063,11 @@ var hovergalleryDirective = ({ props, slots }) => {
12008
12063
  var hovergallery_default = hovergalleryDirective;
12009
12064
 
12010
12065
  // vanilla/directives/chat.ts
12011
- var CHAT_THEME_TOKENS = /* @__PURE__ */ new Set([
12012
- "primary",
12013
- "secondary",
12014
- "accent",
12015
- "neutral",
12016
- "info",
12017
- "success",
12018
- "warning",
12019
- "error"
12020
- ]);
12021
- function isArbitraryColor(value) {
12022
- if (CHAT_THEME_TOKENS.has(value)) return false;
12023
- if (/^(#|rgb|hsl|oklch|oklab|lab|lch|color\()/i.test(value)) return true;
12024
- if (/^[a-zA-Z]+$/.test(value)) return true;
12025
- return false;
12026
- }
12027
12066
  var chatItemDirective = ({ props, renderSlot }) => {
12028
12067
  const side = props.side === "end" ? "end" : "start";
12029
12068
  const wrap = document.createElement("div");
12030
12069
  wrap.className = `nr-chat nr-chat--${side}`;
12031
- if (props.class) wrap.classList.add(...props.class.split(/\s+/).filter(Boolean));
12032
- if (props.style) wrap.setAttribute("style", props.style);
12070
+ applyBaseProps(wrap, props);
12033
12071
  const header = document.createElement("div");
12034
12072
  header.className = "nr-chat__header";
12035
12073
  if (props.name) {
@@ -12054,14 +12092,8 @@ var chatItemDirective = ({ props, renderSlot }) => {
12054
12092
  avatar.appendChild(img);
12055
12093
  wrap.appendChild(avatar);
12056
12094
  }
12057
- const isThemeToken = CHAT_THEME_TOKENS.has(props.color || "");
12058
- const colorClass = isThemeToken ? ` nr-chat__bubble--${props.color}` : "";
12059
12095
  const bubble = document.createElement("div");
12060
- bubble.className = `nr-chat__bubble${colorClass}`;
12061
- if (props.color && isArbitraryColor(props.color) && !isThemeToken) {
12062
- bubble.style.background = props.color;
12063
- bubble.style.color = "white";
12064
- }
12096
+ bubble.className = `nr-chat__bubble${applyColor(bubble, props.color, "nr-chat__bubble")}`;
12065
12097
  bubble.appendChild(renderSlot("default"));
12066
12098
  wrap.appendChild(bubble);
12067
12099
  if (props.footer) {
@@ -12075,8 +12107,7 @@ var chatItemDirective = ({ props, renderSlot }) => {
12075
12107
  var chatDirective = ({ props, renderSlot }) => {
12076
12108
  const wrap = document.createElement("div");
12077
12109
  wrap.className = "nr-chat";
12078
- if (props.class) wrap.classList.add(...props.class.split(/\s+/).filter(Boolean));
12079
- if (props.style) wrap.setAttribute("style", props.style);
12110
+ applyBaseProps(wrap, props);
12080
12111
  wrap.appendChild(renderSlot("default"));
12081
12112
  return wrap;
12082
12113
  };
@@ -12112,8 +12143,7 @@ function bindEventProp(el, eventProp) {
12112
12143
  var richlistItemDirective = ({ props, renderSlot }) => {
12113
12144
  const li = document.createElement("li");
12114
12145
  li.className = "nr-richlist__item";
12115
- if (props.class) li.classList.add(...props.class.split(/\s+/).filter(Boolean));
12116
- if (props.style) li.setAttribute("style", props.style);
12146
+ applyBaseProps(li, props);
12117
12147
  if (props.image) {
12118
12148
  const thumb = document.createElement("div");
12119
12149
  thumb.className = "nr-richlist__thumb";
@@ -12175,36 +12205,20 @@ var richlistItemDirective = ({ props, renderSlot }) => {
12175
12205
  var richlistDirective = ({ props, renderSlot }) => {
12176
12206
  const ul = document.createElement("ul");
12177
12207
  ul.className = "nr-richlist";
12178
- if (props.class) ul.classList.add(...props.class.split(/\s+/).filter(Boolean));
12179
- if (props.style) ul.setAttribute("style", props.style);
12208
+ applyBaseProps(ul, props);
12180
12209
  ul.appendChild(renderSlot("default"));
12181
12210
  return ul;
12182
12211
  };
12183
12212
  var richlist_default = richlistDirective;
12184
12213
 
12185
12214
  // vanilla/directives/stat.ts
12186
- var STAT_THEME_TOKENS = /* @__PURE__ */ new Set([
12187
- "primary",
12188
- "secondary",
12189
- "info",
12190
- "success",
12191
- "warning",
12192
- "error"
12193
- ]);
12194
- function isArbitraryColor2(value) {
12195
- if (STAT_THEME_TOKENS.has(value)) return false;
12196
- if (/^(#|rgb|hsl|oklch|oklab|lab|lch|color\()/i.test(value)) return true;
12197
- if (/^[a-zA-Z]+$/.test(value)) return true;
12198
- return false;
12199
- }
12200
12215
  var statDirective = ({ props }) => {
12201
- const isThemeToken = STAT_THEME_TOKENS.has(props.color || "");
12202
- const colorClass = isThemeToken ? ` nr-stat--${props.color}` : "";
12216
+ const statIsThemeToken = isThemeToken(props.color);
12217
+ const colorClass = statIsThemeToken ? ` nr-stat--${props.color}` : "";
12203
12218
  const stat = document.createElement("div");
12204
12219
  stat.className = `nr-stat${colorClass}`;
12205
- if (props.class) stat.classList.add(...props.class.split(/\s+/).filter(Boolean));
12206
- if (props.style) stat.setAttribute("style", props.style);
12207
- const useInlineColor = props.color && isArbitraryColor2(props.color) && !isThemeToken;
12220
+ applyBaseProps(stat, props);
12221
+ const useInlineColor = props.color && isArbitraryColor(props.color) && !statIsThemeToken;
12208
12222
  if (props.icon) {
12209
12223
  const figure = document.createElement("div");
12210
12224
  figure.className = "nr-stat__figure";
@@ -12297,9 +12311,12 @@ function renderHtmlString(html) {
12297
12311
  processedContent = processedContent.replace(
12298
12312
  /<style(?:\s+[^>]*)?>([\s\S]*?)<\/style>/gi,
12299
12313
  (_match, cssContent) => {
12314
+ const trimmed = cssContent.trim();
12315
+ const existing = document.head.querySelector("style[data-nr-global]");
12316
+ if (existing && existing.textContent === trimmed) return "";
12300
12317
  const styleEl = document.createElement("style");
12301
12318
  styleEl.setAttribute("data-nr-global", "");
12302
- styleEl.textContent = cssContent;
12319
+ styleEl.textContent = trimmed;
12303
12320
  document.head.appendChild(styleEl);
12304
12321
  return "";
12305
12322
  }
@@ -12369,19 +12386,14 @@ function renderElement(element, ctx, allElements) {
12369
12386
  switch (element.type) {
12370
12387
  case "header": {
12371
12388
  const tag = `h${element.level}`;
12372
- let text = element.text;
12373
- const alignCenter = text.match(/^->\s*(.+?)\s*<-\s*$/);
12374
- const alignRight = text.match(/^->\s*(.+?)\s*->\s*$/);
12375
- if (alignCenter) text = alignCenter[1];
12376
- else if (alignRight) text = alignRight[1];
12377
12389
  const h = document.createElement(tag);
12378
12390
  h.id = element.id;
12379
12391
  let cls = `md-h${element.level}`;
12380
- if (alignCenter) cls += " text-center";
12381
- if (alignRight) cls += " text-right";
12392
+ if (element.align === "center") cls += " text-center";
12393
+ else if (element.align === "right") cls += " text-right";
12382
12394
  if (element.classes) cls += ` ${element.classes}`;
12383
12395
  h.className = cls;
12384
- h.appendChild(renderInline(text));
12396
+ h.appendChild(renderInline(element.text));
12385
12397
  return h;
12386
12398
  }
12387
12399
  case "paragraph": {