@noirmd/previewer 2.1.4 → 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/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;
@@ -11298,11 +11309,77 @@ function parseInlinePart(part) {
11298
11309
  return document.createTextNode(part);
11299
11310
  }
11300
11311
 
11312
+ // vanilla/utils.ts
11313
+ var THEME_TOKENS = /* @__PURE__ */ new Set([
11314
+ "primary",
11315
+ "secondary",
11316
+ "accent",
11317
+ "neutral",
11318
+ "info",
11319
+ "success",
11320
+ "warning",
11321
+ "error"
11322
+ ]);
11323
+ function isThemeToken(color) {
11324
+ return !!color && THEME_TOKENS.has(color);
11325
+ }
11326
+ function isArbitraryColor(value) {
11327
+ if (THEME_TOKENS.has(value)) return false;
11328
+ if (/^(#|rgb|hsl|oklch|oklab|lab|lch|color\()/i.test(value)) return true;
11329
+ if (/^[a-zA-Z]+$/.test(value)) return true;
11330
+ return false;
11331
+ }
11332
+ function applyBaseProps(el, props) {
11333
+ if (props.class) {
11334
+ el.classList.add(...props.class.split(/\s+/).filter(Boolean));
11335
+ }
11336
+ if (props.style) {
11337
+ const styles = parseCssString(props.style);
11338
+ for (const [key, value] of Object.entries(styles)) {
11339
+ el.style.setProperty(key, String(value));
11340
+ }
11341
+ }
11342
+ }
11343
+ function applyFloatStyle(el, float, width) {
11344
+ if (!float) return;
11345
+ if (float === "left" || float === "right") {
11346
+ el.style.float = float;
11347
+ if (!width) el.style.maxWidth = "50%";
11348
+ el.style.marginInlineStart = float === "right" ? "1rem" : "";
11349
+ el.style.marginInlineEnd = float === "left" ? "1rem" : "";
11350
+ } else if (float === "center") {
11351
+ el.style.marginInline = "auto";
11352
+ }
11353
+ }
11354
+ function applyColor(el, color, classSuffix) {
11355
+ if (!color) return "";
11356
+ if (isThemeToken(color)) {
11357
+ return ` ${classSuffix}--${color}`;
11358
+ }
11359
+ if (isArbitraryColor(color)) {
11360
+ el.style.background = color;
11361
+ el.style.color = "white";
11362
+ }
11363
+ return "";
11364
+ }
11365
+ function openModal(dialog) {
11366
+ if (!dialog.open) {
11367
+ document.body.appendChild(dialog);
11368
+ dialog.showModal();
11369
+ dialog.addEventListener("close", () => dialog.remove(), { once: true });
11370
+ }
11371
+ }
11372
+ var IMG_RE = /!\[([^\]]*)\]\(([^)]+)\)/g;
11373
+ function parseIntProp(value, defaultValue) {
11374
+ if (!value) return defaultValue;
11375
+ const n = parseInt(value, 10);
11376
+ return Number.isNaN(n) ? defaultValue : n;
11377
+ }
11378
+
11301
11379
  // vanilla/directives/admonition.ts
11302
11380
  var admonitionDirective = ({ directiveType, props, renderSlot }) => {
11303
11381
  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);
11382
+ applyBaseProps(el, props);
11306
11383
  const body = el.querySelector(".nr-admonition__body");
11307
11384
  if (body) {
11308
11385
  body.appendChild(renderSlot("default"));
@@ -11318,8 +11395,7 @@ var detailsDirective = ({ props, renderSlot }) => {
11318
11395
  props.icon,
11319
11396
  props.defaultOpen === "true"
11320
11397
  );
11321
- if (props.class) el.classList.add(...props.class.split(/\s+/).filter(Boolean));
11322
- if (props.style) el.setAttribute("style", props.style);
11398
+ applyBaseProps(el, props);
11323
11399
  const body = el.querySelector(".nr-details__body");
11324
11400
  if (body) {
11325
11401
  body.appendChild(renderSlot("default"));
@@ -11332,12 +11408,12 @@ var details_default = detailsDirective;
11332
11408
  var modalDirective = ({ props, renderSlot }) => {
11333
11409
  const label = props.label || props.title || "Open";
11334
11410
  const modalTitle = props.title || "Modal";
11335
- const customClass = props.class || "";
11411
+ const align = props.align || "left";
11336
11412
  const wrapper = document.createElement("div");
11337
- wrapper.className = "nr-modal-trigger";
11413
+ wrapper.className = `nr-modal-trigger${align === "center" ? " nr-modal-trigger--center" : align === "right" ? " nr-modal-trigger--right" : ""}`;
11338
11414
  const btn = document.createElement("button");
11339
11415
  btn.className = `nr-button nr-button--default`;
11340
- if (customClass) btn.classList.add(...customClass.split(/\s+/).filter(Boolean));
11416
+ applyBaseProps(btn, props);
11341
11417
  const icon = props.icon || "open_in_new";
11342
11418
  if (icon) btn.appendChild(createIcon(icon));
11343
11419
  btn.appendChild(document.createTextNode(label));
@@ -11349,15 +11425,7 @@ var modalDirective = ({ props, renderSlot }) => {
11349
11425
  prose.appendChild(renderSlot("default"));
11350
11426
  body.appendChild(prose);
11351
11427
  }
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
- });
11428
+ btn.addEventListener("click", () => openModal(dialog));
11361
11429
  wrapper.appendChild(btn);
11362
11430
  wrapper.appendChild(dialog);
11363
11431
  return wrapper;
@@ -11367,19 +11435,20 @@ var modal_default = modalDirective;
11367
11435
  // vanilla/directives/button.ts
11368
11436
  var buttonDirective = ({ props, renderSlot }) => {
11369
11437
  const url = props.url || props.href || "#";
11370
- const label = props.label;
11438
+ const label = props.label || props.title;
11371
11439
  const icon = props.icon || "near_me";
11372
11440
  const target = props.target || "_blank";
11373
11441
  const customClass = props.class || "";
11442
+ const align = props.align || "left";
11374
11443
  const wrapper = document.createElement("div");
11375
- wrapper.className = "nr-button-wrap";
11444
+ wrapper.className = `nr-button-wrap${align === "center" ? " nr-button-wrap--center" : align === "right" ? " nr-button-wrap--right" : ""}`;
11376
11445
  if (label) {
11377
11446
  const a = document.createElement("a");
11378
11447
  a.href = url;
11379
11448
  a.target = target;
11380
11449
  a.rel = "noopener noreferrer";
11381
11450
  a.className = "nr-button nr-button--default";
11382
- if (customClass) a.classList.add(...customClass.split(/\s+/).filter(Boolean));
11451
+ applyBaseProps(a, props);
11383
11452
  a.appendChild(createIcon(icon));
11384
11453
  a.appendChild(document.createTextNode(label));
11385
11454
  wrapper.appendChild(a);
@@ -11390,7 +11459,7 @@ var buttonDirective = ({ props, renderSlot }) => {
11390
11459
  if (links.length > 0) {
11391
11460
  links.forEach((link) => {
11392
11461
  link.classList.add("nr-button", "nr-button--default");
11393
- if (customClass) link.classList.add(...customClass.split(/\s+/).filter(Boolean));
11462
+ applyBaseProps(link, props);
11394
11463
  });
11395
11464
  wrapper.appendChild(slotContent);
11396
11465
  } else {
@@ -11399,7 +11468,7 @@ var buttonDirective = ({ props, renderSlot }) => {
11399
11468
  a.target = target;
11400
11469
  a.rel = "noopener noreferrer";
11401
11470
  a.className = "nr-button nr-button--default";
11402
- if (customClass) a.classList.add(...customClass.split(/\s+/).filter(Boolean));
11471
+ applyBaseProps(a, props);
11403
11472
  a.appendChild(createIcon(icon));
11404
11473
  a.appendChild(slotContent);
11405
11474
  wrapper.appendChild(a);
@@ -11422,12 +11491,9 @@ var cardDirective = ({
11422
11491
  const { isSingleCard } = options || {};
11423
11492
  const isModal = directiveType === "card-m";
11424
11493
  const isLink = directiveType === "card-b";
11425
- const inlineStyles = props.style ? parseCssString(props.style) : {};
11426
11494
  const card = document.createElement("div");
11427
11495
  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
- }
11496
+ applyBaseProps(card, props);
11431
11497
  if (image) {
11432
11498
  const imgWrap = document.createElement("div");
11433
11499
  imgWrap.className = `nr-card__image${isSingleCard ? " nr-card__image--tall" : ""}`;
@@ -11506,13 +11572,7 @@ var cardDirective = ({
11506
11572
  prose.appendChild(renderSlot("content") || renderSlot("default"));
11507
11573
  modalBody.appendChild(prose);
11508
11574
  }
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
- });
11575
+ card.addEventListener("click", () => openModal(dialog));
11516
11576
  const frag = document.createDocumentFragment();
11517
11577
  frag.appendChild(card);
11518
11578
  frag.appendChild(dialog);
@@ -11560,8 +11620,8 @@ var slideDirective = ({
11560
11620
  if (lines.length === 0) {
11561
11621
  return document.createDocumentFragment();
11562
11622
  }
11563
- const interval = parseInt(props.interval || "3000", 10);
11564
- const speed = parseInt(props.speed || "500", 10);
11623
+ const interval = parseIntProp(props.interval, 3e3);
11624
+ const speed = parseIntProp(props.speed, 500);
11565
11625
  const rawClass = props.class || "";
11566
11626
  const inlineStyle = props.style ? parseCssString(props.style) : {};
11567
11627
  const scopeClass = `sld-${++slideCounter}`;
@@ -11612,10 +11672,11 @@ var slideDirective = ({
11612
11672
  }
11613
11673
  });
11614
11674
  if (lines.length > 1) {
11615
- setInterval(() => {
11675
+ const id = setInterval(() => {
11616
11676
  current = (current + 1) % lines.length;
11617
11677
  track.style.transform = `translateY(${-current * maxH}px)`;
11618
11678
  }, interval);
11679
+ container.dataset.nrIntervalId = String(id);
11619
11680
  }
11620
11681
  return container;
11621
11682
  };
@@ -11625,8 +11686,7 @@ var slide_default = slideDirective;
11625
11686
  var keysDirective = ({ props, slots }) => {
11626
11687
  const wrap = document.createElement("div");
11627
11688
  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);
11689
+ applyBaseProps(wrap, props);
11630
11690
  const sizeClass = props.size ? ` nr-kbd--${props.size}` : "";
11631
11691
  const parts = (slots.default || "").split("+").map((p) => p.trim()).filter(Boolean);
11632
11692
  parts.forEach((part, i) => {
@@ -11650,8 +11710,7 @@ var accordionCounter = 0;
11650
11710
  var accordionItemDirective = ({ props, renderSlot }) => {
11651
11711
  const item = document.createElement("div");
11652
11712
  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);
11713
+ applyBaseProps(item, props);
11655
11714
  const input = document.createElement("input");
11656
11715
  input.type = "radio";
11657
11716
  input.className = "nr-accordion__input";
@@ -11670,8 +11729,7 @@ var accordionItemDirective = ({ props, renderSlot }) => {
11670
11729
  var accordionDirective = ({ props, renderSlot }) => {
11671
11730
  const wrap = document.createElement("div");
11672
11731
  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);
11732
+ applyBaseProps(wrap, props);
11675
11733
  wrap.appendChild(renderSlot("default"));
11676
11734
  const mode = props.mode === "checkbox" ? "checkbox" : "radio";
11677
11735
  const group = `nr-acc-${++accordionCounter}`;
@@ -11684,7 +11742,6 @@ var accordionDirective = ({ props, renderSlot }) => {
11684
11742
  var accordion_default = accordionDirective;
11685
11743
 
11686
11744
  // vanilla/directives/carousel.ts
11687
- var IMG_RE = /!\[([^\]]*)\]\(([^)]+)\)/g;
11688
11745
  var carouselDirective = ({ props, slots }) => {
11689
11746
  const images = [];
11690
11747
  const raw = slots.default || "";
@@ -11700,19 +11757,9 @@ var carouselDirective = ({ props, slots }) => {
11700
11757
  wrap.className = "nr-carousel";
11701
11758
  wrap.tabIndex = 0;
11702
11759
  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);
11760
+ applyBaseProps(wrap, props);
11705
11761
  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
- }
11762
+ applyFloatStyle(wrap, props.float, props.width);
11716
11763
  const viewport = document.createElement("div");
11717
11764
  viewport.className = "nr-carousel__viewport";
11718
11765
  if (props.height) viewport.style.height = props.height;
@@ -11782,11 +11829,10 @@ var DEFAULT_LABELS = ["days", "hours", "min", "sec"];
11782
11829
  var countdownDirective = ({ props }) => {
11783
11830
  const wrap = document.createElement("div");
11784
11831
  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);
11832
+ applyBaseProps(wrap, props);
11787
11833
  const labelParts = (props.labels || "").split("|").map((s) => s.trim());
11788
11834
  const labels = DEFAULT_LABELS.map((label, i) => labelParts[i] || label);
11789
- const digits = parseInt(props.digits || "2", 10);
11835
+ const digits = parseIntProp(props.digits, 2);
11790
11836
  const targetTime = props.target ? new Date(props.target).getTime() : NaN;
11791
11837
  const hasTarget = !Number.isNaN(targetTime);
11792
11838
  const blocks = [];
@@ -11833,13 +11879,15 @@ var countdownDirective = ({ props }) => {
11833
11879
  blocks.push({ value });
11834
11880
  });
11835
11881
  render();
11836
- if (hasTarget) setInterval(render, 1e3);
11882
+ if (hasTarget) {
11883
+ const id = setInterval(render, 1e3);
11884
+ wrap.dataset.nrIntervalId = String(id);
11885
+ }
11837
11886
  return wrap;
11838
11887
  };
11839
11888
  var countdown_default = countdownDirective;
11840
11889
 
11841
11890
  // vanilla/directives/diff.ts
11842
- var IMG_RE2 = /!\[([^\]]*)\]\(([^)]+)\)/g;
11843
11891
  var diffDirective = ({ props, slots }) => {
11844
11892
  let before = (props.before || "").split("#")[0].trim();
11845
11893
  let after = (props.after || "").split("#")[0].trim();
@@ -11847,8 +11895,8 @@ var diffDirective = ({ props, slots }) => {
11847
11895
  const urls = [];
11848
11896
  const raw = slots.default || "";
11849
11897
  let m;
11850
- IMG_RE2.lastIndex = 0;
11851
- while ((m = IMG_RE2.exec(raw)) !== null) {
11898
+ IMG_RE.lastIndex = 0;
11899
+ while ((m = IMG_RE.exec(raw)) !== null) {
11852
11900
  urls.push(m[2].split("#")[0].trim());
11853
11901
  }
11854
11902
  if (!before && urls.length > 0) before = urls[0];
@@ -11861,21 +11909,11 @@ var diffDirective = ({ props, slots }) => {
11861
11909
  figure.className = "nr-diff";
11862
11910
  figure.tabIndex = 0;
11863
11911
  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);
11912
+ applyBaseProps(figure, props);
11866
11913
  if (props.aspect) figure.style.aspectRatio = props.aspect;
11867
11914
  if (props.height) figure.style.height = props.height;
11868
11915
  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
- }
11916
+ applyFloatStyle(figure, props.float, props.width);
11879
11917
  const beforeItem = document.createElement("div");
11880
11918
  beforeItem.className = "nr-diff__item nr-diff__item--before";
11881
11919
  beforeItem.setAttribute("role", "img");
@@ -11938,8 +11976,7 @@ var diff_default = diffDirective;
11938
11976
  var hover3dDirective = ({ props, renderSlot }) => {
11939
11977
  const container = document.createElement("div");
11940
11978
  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);
11979
+ applyBaseProps(container, props);
11943
11980
  const stage = document.createElement("div");
11944
11981
  stage.className = "nr-hover-3d__stage";
11945
11982
  stage.appendChild(renderSlot("default"));
@@ -11952,14 +11989,13 @@ var hover3dDirective = ({ props, renderSlot }) => {
11952
11989
  var hover3d_default = hover3dDirective;
11953
11990
 
11954
11991
  // vanilla/directives/hovergallery.ts
11955
- var IMG_RE3 = /!\[([^\]]*)\]\(([^)]+)\)/g;
11956
11992
  var MAX_IMAGES = 10;
11957
11993
  var hovergalleryDirective = ({ props, slots }) => {
11958
11994
  const images = [];
11959
11995
  const raw = slots.default || "";
11960
11996
  let m;
11961
- IMG_RE3.lastIndex = 0;
11962
- while ((m = IMG_RE3.exec(raw)) !== null) {
11997
+ IMG_RE.lastIndex = 0;
11998
+ while ((m = IMG_RE.exec(raw)) !== null) {
11963
11999
  images.push({ src: m[2].split("#")[0].trim(), alt: m[1].trim() || "gallery image" });
11964
12000
  }
11965
12001
  if (images.length === 0) {
@@ -11968,9 +12004,8 @@ var hovergalleryDirective = ({ props, slots }) => {
11968
12004
  const count = Math.min(images.length, MAX_IMAGES);
11969
12005
  const figure = document.createElement("figure");
11970
12006
  figure.className = "nr-hover-gallery";
12007
+ applyBaseProps(figure, props);
11971
12008
  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
12009
  const imgEls = [];
11975
12010
  for (let i = 0; i < count; i++) {
11976
12011
  const el = document.createElement("img");
@@ -12020,28 +12055,11 @@ var hovergalleryDirective = ({ props, slots }) => {
12020
12055
  var hovergallery_default = hovergalleryDirective;
12021
12056
 
12022
12057
  // 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
12058
  var chatItemDirective = ({ props, renderSlot }) => {
12040
12059
  const side = props.side === "end" ? "end" : "start";
12041
12060
  const wrap = document.createElement("div");
12042
12061
  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);
12062
+ applyBaseProps(wrap, props);
12045
12063
  const header = document.createElement("div");
12046
12064
  header.className = "nr-chat__header";
12047
12065
  if (props.name) {
@@ -12066,14 +12084,8 @@ var chatItemDirective = ({ props, renderSlot }) => {
12066
12084
  avatar.appendChild(img);
12067
12085
  wrap.appendChild(avatar);
12068
12086
  }
12069
- const isThemeToken = CHAT_THEME_TOKENS.has(props.color || "");
12070
- const colorClass = isThemeToken ? ` nr-chat__bubble--${props.color}` : "";
12071
12087
  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
- }
12088
+ bubble.className = `nr-chat__bubble${applyColor(bubble, props.color, "nr-chat__bubble")}`;
12077
12089
  bubble.appendChild(renderSlot("default"));
12078
12090
  wrap.appendChild(bubble);
12079
12091
  if (props.footer) {
@@ -12087,8 +12099,7 @@ var chatItemDirective = ({ props, renderSlot }) => {
12087
12099
  var chatDirective = ({ props, renderSlot }) => {
12088
12100
  const wrap = document.createElement("div");
12089
12101
  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);
12102
+ applyBaseProps(wrap, props);
12092
12103
  wrap.appendChild(renderSlot("default"));
12093
12104
  return wrap;
12094
12105
  };
@@ -12124,8 +12135,7 @@ function bindEventProp(el, eventProp) {
12124
12135
  var richlistItemDirective = ({ props, renderSlot }) => {
12125
12136
  const li = document.createElement("li");
12126
12137
  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);
12138
+ applyBaseProps(li, props);
12129
12139
  if (props.image) {
12130
12140
  const thumb = document.createElement("div");
12131
12141
  thumb.className = "nr-richlist__thumb";
@@ -12187,36 +12197,20 @@ var richlistItemDirective = ({ props, renderSlot }) => {
12187
12197
  var richlistDirective = ({ props, renderSlot }) => {
12188
12198
  const ul = document.createElement("ul");
12189
12199
  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);
12200
+ applyBaseProps(ul, props);
12192
12201
  ul.appendChild(renderSlot("default"));
12193
12202
  return ul;
12194
12203
  };
12195
12204
  var richlist_default = richlistDirective;
12196
12205
 
12197
12206
  // 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
12207
  var statDirective = ({ props }) => {
12213
- const isThemeToken = STAT_THEME_TOKENS.has(props.color || "");
12214
- const colorClass = isThemeToken ? ` nr-stat--${props.color}` : "";
12208
+ const statIsThemeToken = isThemeToken(props.color);
12209
+ const colorClass = statIsThemeToken ? ` nr-stat--${props.color}` : "";
12215
12210
  const stat = document.createElement("div");
12216
12211
  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;
12212
+ applyBaseProps(stat, props);
12213
+ const useInlineColor = props.color && isArbitraryColor(props.color) && !statIsThemeToken;
12220
12214
  if (props.icon) {
12221
12215
  const figure = document.createElement("div");
12222
12216
  figure.className = "nr-stat__figure";
@@ -12309,9 +12303,12 @@ function renderHtmlString(html) {
12309
12303
  processedContent = processedContent.replace(
12310
12304
  /<style(?:\s+[^>]*)?>([\s\S]*?)<\/style>/gi,
12311
12305
  (_match, cssContent) => {
12306
+ const trimmed = cssContent.trim();
12307
+ const existing = document.head.querySelector("style[data-nr-global]");
12308
+ if (existing && existing.textContent === trimmed) return "";
12312
12309
  const styleEl = document.createElement("style");
12313
12310
  styleEl.setAttribute("data-nr-global", "");
12314
- styleEl.textContent = cssContent;
12311
+ styleEl.textContent = trimmed;
12315
12312
  document.head.appendChild(styleEl);
12316
12313
  return "";
12317
12314
  }
@@ -12381,19 +12378,14 @@ function renderElement(element, ctx, allElements) {
12381
12378
  switch (element.type) {
12382
12379
  case "header": {
12383
12380
  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
12381
  const h = document.createElement(tag);
12390
12382
  h.id = element.id;
12391
12383
  let cls = `md-h${element.level}`;
12392
- if (alignCenter) cls += " text-center";
12393
- if (alignRight) cls += " text-right";
12384
+ if (element.align === "center") cls += " text-center";
12385
+ else if (element.align === "right") cls += " text-right";
12394
12386
  if (element.classes) cls += ` ${element.classes}`;
12395
12387
  h.className = cls;
12396
- h.appendChild(renderInline(text));
12388
+ h.appendChild(renderInline(element.text));
12397
12389
  return h;
12398
12390
  }
12399
12391
  case "paragraph": {