@noirmd/previewer 2.1.3 → 2.1.5

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;
@@ -11286,11 +11297,77 @@ function parseInlinePart(part) {
11286
11297
  return document.createTextNode(part);
11287
11298
  }
11288
11299
 
11300
+ // vanilla/utils.ts
11301
+ var THEME_TOKENS = /* @__PURE__ */ new Set([
11302
+ "primary",
11303
+ "secondary",
11304
+ "accent",
11305
+ "neutral",
11306
+ "info",
11307
+ "success",
11308
+ "warning",
11309
+ "error"
11310
+ ]);
11311
+ function isThemeToken(color) {
11312
+ return !!color && THEME_TOKENS.has(color);
11313
+ }
11314
+ function isArbitraryColor(value) {
11315
+ if (THEME_TOKENS.has(value)) return false;
11316
+ if (/^(#|rgb|hsl|oklch|oklab|lab|lch|color\()/i.test(value)) return true;
11317
+ if (/^[a-zA-Z]+$/.test(value)) return true;
11318
+ return false;
11319
+ }
11320
+ function applyBaseProps(el, props) {
11321
+ if (props.class) {
11322
+ el.classList.add(...props.class.split(/\s+/).filter(Boolean));
11323
+ }
11324
+ if (props.style) {
11325
+ const styles = parseCssString(props.style);
11326
+ for (const [key, value] of Object.entries(styles)) {
11327
+ el.style.setProperty(key, String(value));
11328
+ }
11329
+ }
11330
+ }
11331
+ function applyFloatStyle(el, float, width) {
11332
+ if (!float) return;
11333
+ if (float === "left" || float === "right") {
11334
+ el.style.float = float;
11335
+ if (!width) el.style.maxWidth = "50%";
11336
+ el.style.marginInlineStart = float === "right" ? "1rem" : "";
11337
+ el.style.marginInlineEnd = float === "left" ? "1rem" : "";
11338
+ } else if (float === "center") {
11339
+ el.style.marginInline = "auto";
11340
+ }
11341
+ }
11342
+ function applyColor(el, color, classSuffix) {
11343
+ if (!color) return "";
11344
+ if (isThemeToken(color)) {
11345
+ return ` ${classSuffix}--${color}`;
11346
+ }
11347
+ if (isArbitraryColor(color)) {
11348
+ el.style.background = color;
11349
+ el.style.color = "white";
11350
+ }
11351
+ return "";
11352
+ }
11353
+ function openModal(dialog) {
11354
+ if (!dialog.open) {
11355
+ document.body.appendChild(dialog);
11356
+ dialog.showModal();
11357
+ dialog.addEventListener("close", () => dialog.remove(), { once: true });
11358
+ }
11359
+ }
11360
+ var IMG_RE = /!\[([^\]]*)\]\(([^)]+)\)/g;
11361
+ function parseIntProp(value, defaultValue) {
11362
+ if (!value) return defaultValue;
11363
+ const n = parseInt(value, 10);
11364
+ return Number.isNaN(n) ? defaultValue : n;
11365
+ }
11366
+
11289
11367
  // vanilla/directives/admonition.ts
11290
11368
  var admonitionDirective = ({ directiveType, props, renderSlot }) => {
11291
11369
  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);
11370
+ applyBaseProps(el, props);
11294
11371
  const body = el.querySelector(".nr-admonition__body");
11295
11372
  if (body) {
11296
11373
  body.appendChild(renderSlot("default"));
@@ -11306,8 +11383,7 @@ var detailsDirective = ({ props, renderSlot }) => {
11306
11383
  props.icon,
11307
11384
  props.defaultOpen === "true"
11308
11385
  );
11309
- if (props.class) el.classList.add(...props.class.split(/\s+/).filter(Boolean));
11310
- if (props.style) el.setAttribute("style", props.style);
11386
+ applyBaseProps(el, props);
11311
11387
  const body = el.querySelector(".nr-details__body");
11312
11388
  if (body) {
11313
11389
  body.appendChild(renderSlot("default"));
@@ -11320,12 +11396,12 @@ var details_default = detailsDirective;
11320
11396
  var modalDirective = ({ props, renderSlot }) => {
11321
11397
  const label = props.label || props.title || "Open";
11322
11398
  const modalTitle = props.title || "Modal";
11323
- const customClass = props.class || "";
11399
+ const align = props.align || "left";
11324
11400
  const wrapper = document.createElement("div");
11325
- wrapper.className = "nr-modal-trigger";
11401
+ wrapper.className = `nr-modal-trigger${align === "center" ? " nr-modal-trigger--center" : align === "right" ? " nr-modal-trigger--right" : ""}`;
11326
11402
  const btn = document.createElement("button");
11327
11403
  btn.className = `nr-button nr-button--default`;
11328
- if (customClass) btn.classList.add(...customClass.split(/\s+/).filter(Boolean));
11404
+ applyBaseProps(btn, props);
11329
11405
  const icon = props.icon || "open_in_new";
11330
11406
  if (icon) btn.appendChild(createIcon(icon));
11331
11407
  btn.appendChild(document.createTextNode(label));
@@ -11337,15 +11413,7 @@ var modalDirective = ({ props, renderSlot }) => {
11337
11413
  prose.appendChild(renderSlot("default"));
11338
11414
  body.appendChild(prose);
11339
11415
  }
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
- });
11416
+ btn.addEventListener("click", () => openModal(dialog));
11349
11417
  wrapper.appendChild(btn);
11350
11418
  wrapper.appendChild(dialog);
11351
11419
  return wrapper;
@@ -11355,19 +11423,20 @@ var modal_default = modalDirective;
11355
11423
  // vanilla/directives/button.ts
11356
11424
  var buttonDirective = ({ props, renderSlot }) => {
11357
11425
  const url = props.url || props.href || "#";
11358
- const label = props.label;
11426
+ const label = props.label || props.title;
11359
11427
  const icon = props.icon || "near_me";
11360
11428
  const target = props.target || "_blank";
11361
11429
  const customClass = props.class || "";
11430
+ const align = props.align || "left";
11362
11431
  const wrapper = document.createElement("div");
11363
- wrapper.className = "nr-button-wrap";
11432
+ wrapper.className = `nr-button-wrap${align === "center" ? " nr-button-wrap--center" : align === "right" ? " nr-button-wrap--right" : ""}`;
11364
11433
  if (label) {
11365
11434
  const a = document.createElement("a");
11366
11435
  a.href = url;
11367
11436
  a.target = target;
11368
11437
  a.rel = "noopener noreferrer";
11369
11438
  a.className = "nr-button nr-button--default";
11370
- if (customClass) a.classList.add(...customClass.split(/\s+/).filter(Boolean));
11439
+ applyBaseProps(a, props);
11371
11440
  a.appendChild(createIcon(icon));
11372
11441
  a.appendChild(document.createTextNode(label));
11373
11442
  wrapper.appendChild(a);
@@ -11378,7 +11447,7 @@ var buttonDirective = ({ props, renderSlot }) => {
11378
11447
  if (links.length > 0) {
11379
11448
  links.forEach((link) => {
11380
11449
  link.classList.add("nr-button", "nr-button--default");
11381
- if (customClass) link.classList.add(...customClass.split(/\s+/).filter(Boolean));
11450
+ applyBaseProps(link, props);
11382
11451
  });
11383
11452
  wrapper.appendChild(slotContent);
11384
11453
  } else {
@@ -11387,7 +11456,7 @@ var buttonDirective = ({ props, renderSlot }) => {
11387
11456
  a.target = target;
11388
11457
  a.rel = "noopener noreferrer";
11389
11458
  a.className = "nr-button nr-button--default";
11390
- if (customClass) a.classList.add(...customClass.split(/\s+/).filter(Boolean));
11459
+ applyBaseProps(a, props);
11391
11460
  a.appendChild(createIcon(icon));
11392
11461
  a.appendChild(slotContent);
11393
11462
  wrapper.appendChild(a);
@@ -11410,12 +11479,9 @@ var cardDirective = ({
11410
11479
  const { isSingleCard } = options || {};
11411
11480
  const isModal = directiveType === "card-m";
11412
11481
  const isLink = directiveType === "card-b";
11413
- const inlineStyles = props.style ? parseCssString(props.style) : {};
11414
11482
  const card = document.createElement("div");
11415
11483
  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
- }
11484
+ applyBaseProps(card, props);
11419
11485
  if (image) {
11420
11486
  const imgWrap = document.createElement("div");
11421
11487
  imgWrap.className = `nr-card__image${isSingleCard ? " nr-card__image--tall" : ""}`;
@@ -11494,13 +11560,7 @@ var cardDirective = ({
11494
11560
  prose.appendChild(renderSlot("content") || renderSlot("default"));
11495
11561
  modalBody.appendChild(prose);
11496
11562
  }
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
- });
11563
+ card.addEventListener("click", () => openModal(dialog));
11504
11564
  const frag = document.createDocumentFragment();
11505
11565
  frag.appendChild(card);
11506
11566
  frag.appendChild(dialog);
@@ -11548,8 +11608,8 @@ var slideDirective = ({
11548
11608
  if (lines.length === 0) {
11549
11609
  return document.createDocumentFragment();
11550
11610
  }
11551
- const interval = parseInt(props.interval || "3000", 10);
11552
- const speed = parseInt(props.speed || "500", 10);
11611
+ const interval = parseIntProp(props.interval, 3e3);
11612
+ const speed = parseIntProp(props.speed, 500);
11553
11613
  const rawClass = props.class || "";
11554
11614
  const inlineStyle = props.style ? parseCssString(props.style) : {};
11555
11615
  const scopeClass = `sld-${++slideCounter}`;
@@ -11600,10 +11660,11 @@ var slideDirective = ({
11600
11660
  }
11601
11661
  });
11602
11662
  if (lines.length > 1) {
11603
- setInterval(() => {
11663
+ const id = setInterval(() => {
11604
11664
  current = (current + 1) % lines.length;
11605
11665
  track.style.transform = `translateY(${-current * maxH}px)`;
11606
11666
  }, interval);
11667
+ container.dataset.nrIntervalId = String(id);
11607
11668
  }
11608
11669
  return container;
11609
11670
  };
@@ -11613,8 +11674,7 @@ var slide_default = slideDirective;
11613
11674
  var keysDirective = ({ props, slots }) => {
11614
11675
  const wrap = document.createElement("div");
11615
11676
  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);
11677
+ applyBaseProps(wrap, props);
11618
11678
  const sizeClass = props.size ? ` nr-kbd--${props.size}` : "";
11619
11679
  const parts = (slots.default || "").split("+").map((p) => p.trim()).filter(Boolean);
11620
11680
  parts.forEach((part, i) => {
@@ -11638,8 +11698,7 @@ var accordionCounter = 0;
11638
11698
  var accordionItemDirective = ({ props, renderSlot }) => {
11639
11699
  const item = document.createElement("div");
11640
11700
  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);
11701
+ applyBaseProps(item, props);
11643
11702
  const input = document.createElement("input");
11644
11703
  input.type = "radio";
11645
11704
  input.className = "nr-accordion__input";
@@ -11658,8 +11717,7 @@ var accordionItemDirective = ({ props, renderSlot }) => {
11658
11717
  var accordionDirective = ({ props, renderSlot }) => {
11659
11718
  const wrap = document.createElement("div");
11660
11719
  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);
11720
+ applyBaseProps(wrap, props);
11663
11721
  wrap.appendChild(renderSlot("default"));
11664
11722
  const mode = props.mode === "checkbox" ? "checkbox" : "radio";
11665
11723
  const group = `nr-acc-${++accordionCounter}`;
@@ -11672,7 +11730,6 @@ var accordionDirective = ({ props, renderSlot }) => {
11672
11730
  var accordion_default = accordionDirective;
11673
11731
 
11674
11732
  // vanilla/directives/carousel.ts
11675
- var IMG_RE = /!\[([^\]]*)\]\(([^)]+)\)/g;
11676
11733
  var carouselDirective = ({ props, slots }) => {
11677
11734
  const images = [];
11678
11735
  const raw = slots.default || "";
@@ -11688,19 +11745,9 @@ var carouselDirective = ({ props, slots }) => {
11688
11745
  wrap.className = "nr-carousel";
11689
11746
  wrap.tabIndex = 0;
11690
11747
  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);
11748
+ applyBaseProps(wrap, props);
11693
11749
  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
- }
11750
+ applyFloatStyle(wrap, props.float, props.width);
11704
11751
  const viewport = document.createElement("div");
11705
11752
  viewport.className = "nr-carousel__viewport";
11706
11753
  if (props.height) viewport.style.height = props.height;
@@ -11770,11 +11817,10 @@ var DEFAULT_LABELS = ["days", "hours", "min", "sec"];
11770
11817
  var countdownDirective = ({ props }) => {
11771
11818
  const wrap = document.createElement("div");
11772
11819
  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);
11820
+ applyBaseProps(wrap, props);
11775
11821
  const labelParts = (props.labels || "").split("|").map((s) => s.trim());
11776
11822
  const labels = DEFAULT_LABELS.map((label, i) => labelParts[i] || label);
11777
- const digits = parseInt(props.digits || "2", 10);
11823
+ const digits = parseIntProp(props.digits, 2);
11778
11824
  const targetTime = props.target ? new Date(props.target).getTime() : NaN;
11779
11825
  const hasTarget = !Number.isNaN(targetTime);
11780
11826
  const blocks = [];
@@ -11821,13 +11867,15 @@ var countdownDirective = ({ props }) => {
11821
11867
  blocks.push({ value });
11822
11868
  });
11823
11869
  render();
11824
- if (hasTarget) setInterval(render, 1e3);
11870
+ if (hasTarget) {
11871
+ const id = setInterval(render, 1e3);
11872
+ wrap.dataset.nrIntervalId = String(id);
11873
+ }
11825
11874
  return wrap;
11826
11875
  };
11827
11876
  var countdown_default = countdownDirective;
11828
11877
 
11829
11878
  // vanilla/directives/diff.ts
11830
- var IMG_RE2 = /!\[([^\]]*)\]\(([^)]+)\)/g;
11831
11879
  var diffDirective = ({ props, slots }) => {
11832
11880
  let before = (props.before || "").split("#")[0].trim();
11833
11881
  let after = (props.after || "").split("#")[0].trim();
@@ -11835,8 +11883,8 @@ var diffDirective = ({ props, slots }) => {
11835
11883
  const urls = [];
11836
11884
  const raw = slots.default || "";
11837
11885
  let m;
11838
- IMG_RE2.lastIndex = 0;
11839
- while ((m = IMG_RE2.exec(raw)) !== null) {
11886
+ IMG_RE.lastIndex = 0;
11887
+ while ((m = IMG_RE.exec(raw)) !== null) {
11840
11888
  urls.push(m[2].split("#")[0].trim());
11841
11889
  }
11842
11890
  if (!before && urls.length > 0) before = urls[0];
@@ -11849,21 +11897,11 @@ var diffDirective = ({ props, slots }) => {
11849
11897
  figure.className = "nr-diff";
11850
11898
  figure.tabIndex = 0;
11851
11899
  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);
11900
+ applyBaseProps(figure, props);
11854
11901
  if (props.aspect) figure.style.aspectRatio = props.aspect;
11855
11902
  if (props.height) figure.style.height = props.height;
11856
11903
  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
- }
11904
+ applyFloatStyle(figure, props.float, props.width);
11867
11905
  const beforeItem = document.createElement("div");
11868
11906
  beforeItem.className = "nr-diff__item nr-diff__item--before";
11869
11907
  beforeItem.setAttribute("role", "img");
@@ -11926,8 +11964,7 @@ var diff_default = diffDirective;
11926
11964
  var hover3dDirective = ({ props, renderSlot }) => {
11927
11965
  const container = document.createElement("div");
11928
11966
  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);
11967
+ applyBaseProps(container, props);
11931
11968
  const stage = document.createElement("div");
11932
11969
  stage.className = "nr-hover-3d__stage";
11933
11970
  stage.appendChild(renderSlot("default"));
@@ -11940,14 +11977,13 @@ var hover3dDirective = ({ props, renderSlot }) => {
11940
11977
  var hover3d_default = hover3dDirective;
11941
11978
 
11942
11979
  // vanilla/directives/hovergallery.ts
11943
- var IMG_RE3 = /!\[([^\]]*)\]\(([^)]+)\)/g;
11944
11980
  var MAX_IMAGES = 10;
11945
11981
  var hovergalleryDirective = ({ props, slots }) => {
11946
11982
  const images = [];
11947
11983
  const raw = slots.default || "";
11948
11984
  let m;
11949
- IMG_RE3.lastIndex = 0;
11950
- while ((m = IMG_RE3.exec(raw)) !== null) {
11985
+ IMG_RE.lastIndex = 0;
11986
+ while ((m = IMG_RE.exec(raw)) !== null) {
11951
11987
  images.push({ src: m[2].split("#")[0].trim(), alt: m[1].trim() || "gallery image" });
11952
11988
  }
11953
11989
  if (images.length === 0) {
@@ -11956,9 +11992,8 @@ var hovergalleryDirective = ({ props, slots }) => {
11956
11992
  const count = Math.min(images.length, MAX_IMAGES);
11957
11993
  const figure = document.createElement("figure");
11958
11994
  figure.className = "nr-hover-gallery";
11995
+ applyBaseProps(figure, props);
11959
11996
  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
11997
  const imgEls = [];
11963
11998
  for (let i = 0; i < count; i++) {
11964
11999
  const el = document.createElement("img");
@@ -12008,28 +12043,11 @@ var hovergalleryDirective = ({ props, slots }) => {
12008
12043
  var hovergallery_default = hovergalleryDirective;
12009
12044
 
12010
12045
  // 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
12046
  var chatItemDirective = ({ props, renderSlot }) => {
12028
12047
  const side = props.side === "end" ? "end" : "start";
12029
12048
  const wrap = document.createElement("div");
12030
12049
  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);
12050
+ applyBaseProps(wrap, props);
12033
12051
  const header = document.createElement("div");
12034
12052
  header.className = "nr-chat__header";
12035
12053
  if (props.name) {
@@ -12054,14 +12072,8 @@ var chatItemDirective = ({ props, renderSlot }) => {
12054
12072
  avatar.appendChild(img);
12055
12073
  wrap.appendChild(avatar);
12056
12074
  }
12057
- const isThemeToken = CHAT_THEME_TOKENS.has(props.color || "");
12058
- const colorClass = isThemeToken ? ` nr-chat__bubble--${props.color}` : "";
12059
12075
  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
- }
12076
+ bubble.className = `nr-chat__bubble${applyColor(bubble, props.color, "nr-chat__bubble")}`;
12065
12077
  bubble.appendChild(renderSlot("default"));
12066
12078
  wrap.appendChild(bubble);
12067
12079
  if (props.footer) {
@@ -12075,8 +12087,7 @@ var chatItemDirective = ({ props, renderSlot }) => {
12075
12087
  var chatDirective = ({ props, renderSlot }) => {
12076
12088
  const wrap = document.createElement("div");
12077
12089
  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);
12090
+ applyBaseProps(wrap, props);
12080
12091
  wrap.appendChild(renderSlot("default"));
12081
12092
  return wrap;
12082
12093
  };
@@ -12112,8 +12123,7 @@ function bindEventProp(el, eventProp) {
12112
12123
  var richlistItemDirective = ({ props, renderSlot }) => {
12113
12124
  const li = document.createElement("li");
12114
12125
  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);
12126
+ applyBaseProps(li, props);
12117
12127
  if (props.image) {
12118
12128
  const thumb = document.createElement("div");
12119
12129
  thumb.className = "nr-richlist__thumb";
@@ -12175,36 +12185,20 @@ var richlistItemDirective = ({ props, renderSlot }) => {
12175
12185
  var richlistDirective = ({ props, renderSlot }) => {
12176
12186
  const ul = document.createElement("ul");
12177
12187
  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);
12188
+ applyBaseProps(ul, props);
12180
12189
  ul.appendChild(renderSlot("default"));
12181
12190
  return ul;
12182
12191
  };
12183
12192
  var richlist_default = richlistDirective;
12184
12193
 
12185
12194
  // 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
12195
  var statDirective = ({ props }) => {
12201
- const isThemeToken = STAT_THEME_TOKENS.has(props.color || "");
12202
- const colorClass = isThemeToken ? ` nr-stat--${props.color}` : "";
12196
+ const statIsThemeToken = isThemeToken(props.color);
12197
+ const colorClass = statIsThemeToken ? ` nr-stat--${props.color}` : "";
12203
12198
  const stat = document.createElement("div");
12204
12199
  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;
12200
+ applyBaseProps(stat, props);
12201
+ const useInlineColor = props.color && isArbitraryColor(props.color) && !statIsThemeToken;
12208
12202
  if (props.icon) {
12209
12203
  const figure = document.createElement("div");
12210
12204
  figure.className = "nr-stat__figure";
@@ -12297,9 +12291,12 @@ function renderHtmlString(html) {
12297
12291
  processedContent = processedContent.replace(
12298
12292
  /<style(?:\s+[^>]*)?>([\s\S]*?)<\/style>/gi,
12299
12293
  (_match, cssContent) => {
12294
+ const trimmed = cssContent.trim();
12295
+ const existing = document.head.querySelector("style[data-nr-global]");
12296
+ if (existing && existing.textContent === trimmed) return "";
12300
12297
  const styleEl = document.createElement("style");
12301
12298
  styleEl.setAttribute("data-nr-global", "");
12302
- styleEl.textContent = cssContent;
12299
+ styleEl.textContent = trimmed;
12303
12300
  document.head.appendChild(styleEl);
12304
12301
  return "";
12305
12302
  }
@@ -12369,19 +12366,14 @@ function renderElement(element, ctx, allElements) {
12369
12366
  switch (element.type) {
12370
12367
  case "header": {
12371
12368
  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
12369
  const h = document.createElement(tag);
12378
12370
  h.id = element.id;
12379
12371
  let cls = `md-h${element.level}`;
12380
- if (alignCenter) cls += " text-center";
12381
- if (alignRight) cls += " text-right";
12372
+ if (element.align === "center") cls += " text-center";
12373
+ else if (element.align === "right") cls += " text-right";
12382
12374
  if (element.classes) cls += ` ${element.classes}`;
12383
12375
  h.className = cls;
12384
- h.appendChild(renderInline(text));
12376
+ h.appendChild(renderInline(element.text));
12385
12377
  return h;
12386
12378
  }
12387
12379
  case "paragraph": {