@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.
@@ -9,6 +9,7 @@ interface HeaderToken {
9
9
  text: string;
10
10
  id: string;
11
11
  classes?: string;
12
+ align?: 'center' | 'right';
12
13
  }
13
14
  interface ParagraphToken {
14
15
  type: 'paragraph';
package/dist/vanilla.d.ts CHANGED
@@ -9,6 +9,7 @@ interface HeaderToken {
9
9
  text: string;
10
10
  id: string;
11
11
  classes?: string;
12
+ align?: 'center' | 'right';
12
13
  }
13
14
  interface ParagraphToken {
14
15
  type: 'paragraph';
package/dist/vanilla.js CHANGED
@@ -1643,6 +1643,22 @@ function parseHtmlAttrs(attrsString) {
1643
1643
  }
1644
1644
 
1645
1645
  // core/parser.ts
1646
+ var VOID_ELEMENTS = /* @__PURE__ */ new Set([
1647
+ "area",
1648
+ "base",
1649
+ "br",
1650
+ "col",
1651
+ "embed",
1652
+ "hr",
1653
+ "img",
1654
+ "input",
1655
+ "link",
1656
+ "meta",
1657
+ "param",
1658
+ "source",
1659
+ "track",
1660
+ "wbr"
1661
+ ]);
1646
1662
  function parseMarkdown(markdown2) {
1647
1663
  if (!markdown2) return [];
1648
1664
  const lines = markdown2.replace(/\r\n/g, "\n").replace(/\r/g, "").split("\n");
@@ -1656,8 +1672,19 @@ function parseMarkdown(markdown2) {
1656
1672
  if (match = trimmed.match(/^(#{1,6})\s+(.+)$/)) {
1657
1673
  const level = match[1].length;
1658
1674
  const rawText = match[2];
1659
- const { text: text2, classes: classes2, id: customId } = extractAttributes(rawText);
1660
- const baseId = customId || generateId(text2.replace(/->|<-/g, ""));
1675
+ const { text: rawParsedText, classes: classes2, id: customId } = extractAttributes(rawText);
1676
+ let text2 = rawParsedText;
1677
+ let align;
1678
+ const alignCenter = text2.match(/^->\s*(.+?)\s*<-\s*$/);
1679
+ const alignRight = text2.match(/^->\s*(.+?)\s*->\s*$/);
1680
+ if (alignCenter) {
1681
+ text2 = alignCenter[1];
1682
+ align = "center";
1683
+ } else if (alignRight) {
1684
+ text2 = alignRight[1];
1685
+ align = "right";
1686
+ }
1687
+ const baseId = customId || generateId(text2);
1661
1688
  let id2 = baseId;
1662
1689
  let n = 1;
1663
1690
  while (usedIds.has(id2)) {
@@ -1665,7 +1692,7 @@ function parseMarkdown(markdown2) {
1665
1692
  id2 = `${baseId}-${n}`;
1666
1693
  }
1667
1694
  usedIds.add(id2);
1668
- result.push({ type: "header", level, text: text2, id: id2, classes: classes2 || void 0 });
1695
+ result.push({ type: "header", level, text: text2, id: id2, classes: classes2 || void 0, align });
1669
1696
  i++;
1670
1697
  continue;
1671
1698
  }
@@ -1841,29 +1868,13 @@ function parseMarkdown(markdown2) {
1841
1868
  let tagStartMatch = trimmed.match(/^<([a-zA-Z][\w-]*)/);
1842
1869
  if (tagStartMatch) {
1843
1870
  const tagName = tagStartMatch[1].toLowerCase();
1844
- const voidElements = /* @__PURE__ */ new Set([
1845
- "area",
1846
- "base",
1847
- "br",
1848
- "col",
1849
- "embed",
1850
- "hr",
1851
- "img",
1852
- "input",
1853
- "link",
1854
- "meta",
1855
- "param",
1856
- "source",
1857
- "track",
1858
- "wbr"
1859
- ]);
1860
1871
  const remainingText = lines.slice(i).join("\n");
1861
1872
  const openTagRegex = new RegExp(`^\\s*<${tagName}\\b([^>]*?)>`, "i");
1862
1873
  const openTagMatch = remainingText.match(openTagRegex);
1863
1874
  if (openTagMatch) {
1864
1875
  const fullOpenTag = openTagMatch[0];
1865
1876
  const attrs = openTagMatch[1].replace(/\s+/g, " ").trim();
1866
- const isSelfClosing = fullOpenTag.endsWith("/>") || voidElements.has(tagName);
1877
+ const isSelfClosing = fullOpenTag.endsWith("/>") || VOID_ELEMENTS.has(tagName);
1867
1878
  if (isSelfClosing) {
1868
1879
  const blockText = remainingText.substring(0, openTagMatch.index + fullOpenTag.length);
1869
1880
  const consumedLines = blockText.split("\n").length;
@@ -11037,6 +11048,7 @@ function createModal(title) {
11037
11048
  header.appendChild(titleEl);
11038
11049
  const closeBtn = document.createElement("button");
11039
11050
  closeBtn.className = "nr-modal__close";
11051
+ closeBtn.setAttribute("aria-label", "Close");
11040
11052
  closeBtn.appendChild(createIcon("close"));
11041
11053
  closeBtn.addEventListener("click", () => dialog.close());
11042
11054
  header.appendChild(closeBtn);
@@ -11257,11 +11269,89 @@ function parseInlinePart(part) {
11257
11269
  return document.createTextNode(part);
11258
11270
  }
11259
11271
 
11272
+ // vanilla/utils.ts
11273
+ var THEME_TOKENS = /* @__PURE__ */ new Set([
11274
+ "primary",
11275
+ "secondary",
11276
+ "accent",
11277
+ "neutral",
11278
+ "info",
11279
+ "success",
11280
+ "warning",
11281
+ "error"
11282
+ ]);
11283
+ function isThemeToken(color) {
11284
+ return !!color && THEME_TOKENS.has(color);
11285
+ }
11286
+ function isArbitraryColor(value) {
11287
+ if (THEME_TOKENS.has(value)) return false;
11288
+ if (/^(#|rgb|hsl|oklch|oklab|lab|lch|color\()/i.test(value)) return true;
11289
+ if (/^[a-zA-Z]+$/.test(value)) return true;
11290
+ return false;
11291
+ }
11292
+ function applyBaseProps(el, props) {
11293
+ if (props.class) {
11294
+ el.classList.add(...props.class.split(/\s+/).filter(Boolean));
11295
+ }
11296
+ if (props.style) {
11297
+ const styles = parseCssString(props.style);
11298
+ for (const [key, value] of Object.entries(styles)) {
11299
+ el.style.setProperty(key, String(value));
11300
+ }
11301
+ }
11302
+ }
11303
+ function applyFloatStyle(el, float, width) {
11304
+ if (!float) return;
11305
+ if (float === "left" || float === "right") {
11306
+ el.style.float = float;
11307
+ if (!width) el.style.maxWidth = "50%";
11308
+ el.style.marginInlineStart = float === "right" ? "1rem" : "";
11309
+ el.style.marginInlineEnd = float === "left" ? "1rem" : "";
11310
+ } else if (float === "center") {
11311
+ el.style.marginInline = "auto";
11312
+ }
11313
+ }
11314
+ function applyColor(el, color, classSuffix) {
11315
+ if (!color) return "";
11316
+ if (isThemeToken(color)) {
11317
+ return ` ${classSuffix}--${color}`;
11318
+ }
11319
+ if (isArbitraryColor(color)) {
11320
+ el.style.background = color;
11321
+ el.style.color = "white";
11322
+ }
11323
+ return "";
11324
+ }
11325
+ function openModal(dialog) {
11326
+ if (!dialog.open) {
11327
+ document.body.appendChild(dialog);
11328
+ dialog.showModal();
11329
+ dialog.addEventListener("close", () => dialog.remove(), { once: true });
11330
+ }
11331
+ }
11332
+ var IMG_RE = /!\[([^\]]*)\]\(([^)]+)\)/g;
11333
+ function applyAlignClass(el, baseClass, align) {
11334
+ if (align === "center") {
11335
+ el.classList.add(`${baseClass}--center`);
11336
+ } else if (align === "right") {
11337
+ el.classList.add(`${baseClass}--right`);
11338
+ }
11339
+ }
11340
+ function resolveIcon(value, fallback) {
11341
+ if (!value) return fallback;
11342
+ if (value === "none" || value === "off") return null;
11343
+ return value;
11344
+ }
11345
+ function parseIntProp(value, defaultValue) {
11346
+ if (!value) return defaultValue;
11347
+ const n = parseInt(value, 10);
11348
+ return Number.isNaN(n) ? defaultValue : n;
11349
+ }
11350
+
11260
11351
  // vanilla/directives/admonition.ts
11261
11352
  var admonitionDirective = ({ directiveType, props, renderSlot }) => {
11262
11353
  const el = createAdmonition(directiveType, props.title, props.icon);
11263
- if (props.class) el.classList.add(...props.class.split(/\s+/).filter(Boolean));
11264
- if (props.style) el.setAttribute("style", props.style);
11354
+ applyBaseProps(el, props);
11265
11355
  const body = el.querySelector(".nr-admonition__body");
11266
11356
  if (body) {
11267
11357
  body.appendChild(renderSlot("default"));
@@ -11277,8 +11367,7 @@ var detailsDirective = ({ props, renderSlot }) => {
11277
11367
  props.icon,
11278
11368
  props.defaultOpen === "true"
11279
11369
  );
11280
- if (props.class) el.classList.add(...props.class.split(/\s+/).filter(Boolean));
11281
- if (props.style) el.setAttribute("style", props.style);
11370
+ applyBaseProps(el, props);
11282
11371
  const body = el.querySelector(".nr-details__body");
11283
11372
  if (body) {
11284
11373
  body.appendChild(renderSlot("default"));
@@ -11291,14 +11380,17 @@ var details_default = detailsDirective;
11291
11380
  var modalDirective = ({ props, renderSlot }) => {
11292
11381
  const label = props.label || props.title || "Open";
11293
11382
  const modalTitle = props.title || "Modal";
11294
- const customClass = props.class || "";
11383
+ const align = props.align || "left";
11384
+ const iconName = resolveIcon(props.icon, "open_in_full");
11295
11385
  const wrapper = document.createElement("div");
11296
11386
  wrapper.className = "nr-modal-trigger";
11387
+ applyAlignClass(wrapper, "nr-modal-trigger", align);
11297
11388
  const btn = document.createElement("button");
11298
- btn.className = `nr-button nr-button--default`;
11299
- if (customClass) btn.classList.add(...customClass.split(/\s+/).filter(Boolean));
11300
- const icon = props.icon || "open_in_new";
11301
- if (icon) btn.appendChild(createIcon(icon));
11389
+ btn.className = "nr-button nr-button--default";
11390
+ btn.setAttribute("aria-haspopup", "dialog");
11391
+ applyColor(btn, props.color, "nr-button");
11392
+ applyBaseProps(btn, props);
11393
+ if (iconName) btn.appendChild(createIcon(iconName));
11302
11394
  btn.appendChild(document.createTextNode(label));
11303
11395
  const dialog = createModal(modalTitle);
11304
11396
  const body = dialog.querySelector(".nr-modal__body");
@@ -11308,15 +11400,7 @@ var modalDirective = ({ props, renderSlot }) => {
11308
11400
  prose.appendChild(renderSlot("default"));
11309
11401
  body.appendChild(prose);
11310
11402
  }
11311
- btn.addEventListener("click", () => {
11312
- if (!dialog.open) {
11313
- document.body.appendChild(dialog);
11314
- dialog.showModal();
11315
- dialog.addEventListener("close", () => {
11316
- dialog.remove();
11317
- }, { once: true });
11318
- }
11319
- });
11403
+ btn.addEventListener("click", () => openModal(dialog));
11320
11404
  wrapper.appendChild(btn);
11321
11405
  wrapper.appendChild(dialog);
11322
11406
  return wrapper;
@@ -11326,20 +11410,22 @@ var modal_default = modalDirective;
11326
11410
  // vanilla/directives/button.ts
11327
11411
  var buttonDirective = ({ props, renderSlot }) => {
11328
11412
  const url = props.url || props.href || "#";
11329
- const label = props.label;
11330
- const icon = props.icon || "near_me";
11413
+ const label = props.label || props.title;
11414
+ const iconName = resolveIcon(props.icon, "touch_app");
11331
11415
  const target = props.target || "_blank";
11332
- const customClass = props.class || "";
11416
+ const align = props.align || "left";
11333
11417
  const wrapper = document.createElement("div");
11334
11418
  wrapper.className = "nr-button-wrap";
11419
+ applyAlignClass(wrapper, "nr-button-wrap", align);
11335
11420
  if (label) {
11336
11421
  const a = document.createElement("a");
11337
11422
  a.href = url;
11338
11423
  a.target = target;
11339
- a.rel = "noopener noreferrer";
11424
+ if (target === "_blank") a.rel = "noopener noreferrer";
11340
11425
  a.className = "nr-button nr-button--default";
11341
- if (customClass) a.classList.add(...customClass.split(/\s+/).filter(Boolean));
11342
- a.appendChild(createIcon(icon));
11426
+ applyColor(a, props.color, "nr-button");
11427
+ applyBaseProps(a, props);
11428
+ if (iconName) a.appendChild(createIcon(iconName));
11343
11429
  a.appendChild(document.createTextNode(label));
11344
11430
  wrapper.appendChild(a);
11345
11431
  return wrapper;
@@ -11349,17 +11435,20 @@ var buttonDirective = ({ props, renderSlot }) => {
11349
11435
  if (links.length > 0) {
11350
11436
  links.forEach((link) => {
11351
11437
  link.classList.add("nr-button", "nr-button--default");
11352
- if (customClass) link.classList.add(...customClass.split(/\s+/).filter(Boolean));
11438
+ applyColor(link, props.color, "nr-button");
11439
+ if (iconName) link.prepend(createIcon(iconName));
11440
+ applyBaseProps(link, props);
11353
11441
  });
11354
11442
  wrapper.appendChild(slotContent);
11355
11443
  } else {
11356
11444
  const a = document.createElement("a");
11357
11445
  a.href = url;
11358
11446
  a.target = target;
11359
- a.rel = "noopener noreferrer";
11447
+ if (target === "_blank") a.rel = "noopener noreferrer";
11360
11448
  a.className = "nr-button nr-button--default";
11361
- if (customClass) a.classList.add(...customClass.split(/\s+/).filter(Boolean));
11362
- a.appendChild(createIcon(icon));
11449
+ applyColor(a, props.color, "nr-button");
11450
+ applyBaseProps(a, props);
11451
+ if (iconName) a.appendChild(createIcon(iconName));
11363
11452
  a.appendChild(slotContent);
11364
11453
  wrapper.appendChild(a);
11365
11454
  }
@@ -11381,12 +11470,9 @@ var cardDirective = ({
11381
11470
  const { isSingleCard } = options || {};
11382
11471
  const isModal = directiveType === "card-m";
11383
11472
  const isLink = directiveType === "card-b";
11384
- const inlineStyles = props.style ? parseCssString(props.style) : {};
11385
11473
  const card = document.createElement("div");
11386
11474
  card.className = `nr-card${isModal || isLink ? " nr-card--interactive" : ""} ${customClass}`.trim();
11387
- for (const [key, value] of Object.entries(inlineStyles)) {
11388
- card.style.setProperty(key, String(value));
11389
- }
11475
+ applyBaseProps(card, props);
11390
11476
  if (image) {
11391
11477
  const imgWrap = document.createElement("div");
11392
11478
  imgWrap.className = `nr-card__image${isSingleCard ? " nr-card__image--tall" : ""}`;
@@ -11465,13 +11551,7 @@ var cardDirective = ({
11465
11551
  prose.appendChild(renderSlot("content") || renderSlot("default"));
11466
11552
  modalBody.appendChild(prose);
11467
11553
  }
11468
- card.addEventListener("click", () => {
11469
- if (!dialog.open) {
11470
- document.body.appendChild(dialog);
11471
- dialog.showModal();
11472
- dialog.addEventListener("close", () => dialog.remove(), { once: true });
11473
- }
11474
- });
11554
+ card.addEventListener("click", () => openModal(dialog));
11475
11555
  const frag = document.createDocumentFragment();
11476
11556
  frag.appendChild(card);
11477
11557
  frag.appendChild(dialog);
@@ -11519,8 +11599,8 @@ var slideDirective = ({
11519
11599
  if (lines.length === 0) {
11520
11600
  return document.createDocumentFragment();
11521
11601
  }
11522
- const interval = parseInt(props.interval || "3000", 10);
11523
- const speed = parseInt(props.speed || "500", 10);
11602
+ const interval = parseIntProp(props.interval, 3e3);
11603
+ const speed = parseIntProp(props.speed, 500);
11524
11604
  const rawClass = props.class || "";
11525
11605
  const inlineStyle = props.style ? parseCssString(props.style) : {};
11526
11606
  const scopeClass = `sld-${++slideCounter}`;
@@ -11571,10 +11651,11 @@ var slideDirective = ({
11571
11651
  }
11572
11652
  });
11573
11653
  if (lines.length > 1) {
11574
- setInterval(() => {
11654
+ const id = setInterval(() => {
11575
11655
  current = (current + 1) % lines.length;
11576
11656
  track.style.transform = `translateY(${-current * maxH}px)`;
11577
11657
  }, interval);
11658
+ container.dataset.nrIntervalId = String(id);
11578
11659
  }
11579
11660
  return container;
11580
11661
  };
@@ -11584,8 +11665,7 @@ var slide_default = slideDirective;
11584
11665
  var keysDirective = ({ props, slots }) => {
11585
11666
  const wrap = document.createElement("div");
11586
11667
  wrap.className = "nr-keys";
11587
- if (props.class) wrap.classList.add(...props.class.split(/\s+/).filter(Boolean));
11588
- if (props.style) wrap.setAttribute("style", props.style);
11668
+ applyBaseProps(wrap, props);
11589
11669
  const sizeClass = props.size ? ` nr-kbd--${props.size}` : "";
11590
11670
  const parts = (slots.default || "").split("+").map((p) => p.trim()).filter(Boolean);
11591
11671
  parts.forEach((part, i) => {
@@ -11609,8 +11689,7 @@ var accordionCounter = 0;
11609
11689
  var accordionItemDirective = ({ props, renderSlot }) => {
11610
11690
  const item = document.createElement("div");
11611
11691
  item.className = "nr-accordion__item";
11612
- if (props.class) item.classList.add(...props.class.split(/\s+/).filter(Boolean));
11613
- if (props.style) item.setAttribute("style", props.style);
11692
+ applyBaseProps(item, props);
11614
11693
  const input = document.createElement("input");
11615
11694
  input.type = "radio";
11616
11695
  input.className = "nr-accordion__input";
@@ -11629,8 +11708,7 @@ var accordionItemDirective = ({ props, renderSlot }) => {
11629
11708
  var accordionDirective = ({ props, renderSlot }) => {
11630
11709
  const wrap = document.createElement("div");
11631
11710
  wrap.className = "nr-accordion";
11632
- if (props.class) wrap.classList.add(...props.class.split(/\s+/).filter(Boolean));
11633
- if (props.style) wrap.setAttribute("style", props.style);
11711
+ applyBaseProps(wrap, props);
11634
11712
  wrap.appendChild(renderSlot("default"));
11635
11713
  const mode = props.mode === "checkbox" ? "checkbox" : "radio";
11636
11714
  const group = `nr-acc-${++accordionCounter}`;
@@ -11643,7 +11721,6 @@ var accordionDirective = ({ props, renderSlot }) => {
11643
11721
  var accordion_default = accordionDirective;
11644
11722
 
11645
11723
  // vanilla/directives/carousel.ts
11646
- var IMG_RE = /!\[([^\]]*)\]\(([^)]+)\)/g;
11647
11724
  var carouselDirective = ({ props, slots }) => {
11648
11725
  const images = [];
11649
11726
  const raw = slots.default || "";
@@ -11659,19 +11736,9 @@ var carouselDirective = ({ props, slots }) => {
11659
11736
  wrap.className = "nr-carousel";
11660
11737
  wrap.tabIndex = 0;
11661
11738
  wrap.setAttribute("aria-label", "Image carousel");
11662
- if (props.class) wrap.classList.add(...props.class.split(/\s+/).filter(Boolean));
11663
- if (props.style) wrap.setAttribute("style", props.style);
11739
+ applyBaseProps(wrap, props);
11664
11740
  if (props.width) wrap.style.width = props.width;
11665
- if (props.float) {
11666
- if (props.float === "left" || props.float === "right") {
11667
- wrap.style.float = props.float;
11668
- if (!props.width) wrap.style.maxWidth = "50%";
11669
- wrap.style.marginInlineStart = props.float === "right" ? "1rem" : "";
11670
- wrap.style.marginInlineEnd = props.float === "left" ? "1rem" : "";
11671
- } else if (props.float === "center") {
11672
- wrap.style.marginInline = "auto";
11673
- }
11674
- }
11741
+ applyFloatStyle(wrap, props.float, props.width);
11675
11742
  const viewport = document.createElement("div");
11676
11743
  viewport.className = "nr-carousel__viewport";
11677
11744
  if (props.height) viewport.style.height = props.height;
@@ -11741,11 +11808,10 @@ var DEFAULT_LABELS = ["days", "hours", "min", "sec"];
11741
11808
  var countdownDirective = ({ props }) => {
11742
11809
  const wrap = document.createElement("div");
11743
11810
  wrap.className = "nr-countdown";
11744
- if (props.class) wrap.classList.add(...props.class.split(/\s+/).filter(Boolean));
11745
- if (props.style) wrap.setAttribute("style", props.style);
11811
+ applyBaseProps(wrap, props);
11746
11812
  const labelParts = (props.labels || "").split("|").map((s) => s.trim());
11747
11813
  const labels = DEFAULT_LABELS.map((label, i) => labelParts[i] || label);
11748
- const digits = parseInt(props.digits || "2", 10);
11814
+ const digits = parseIntProp(props.digits, 2);
11749
11815
  const targetTime = props.target ? new Date(props.target).getTime() : NaN;
11750
11816
  const hasTarget = !Number.isNaN(targetTime);
11751
11817
  const blocks = [];
@@ -11792,13 +11858,15 @@ var countdownDirective = ({ props }) => {
11792
11858
  blocks.push({ value });
11793
11859
  });
11794
11860
  render();
11795
- if (hasTarget) setInterval(render, 1e3);
11861
+ if (hasTarget) {
11862
+ const id = setInterval(render, 1e3);
11863
+ wrap.dataset.nrIntervalId = String(id);
11864
+ }
11796
11865
  return wrap;
11797
11866
  };
11798
11867
  var countdown_default = countdownDirective;
11799
11868
 
11800
11869
  // vanilla/directives/diff.ts
11801
- var IMG_RE2 = /!\[([^\]]*)\]\(([^)]+)\)/g;
11802
11870
  var diffDirective = ({ props, slots }) => {
11803
11871
  let before = (props.before || "").split("#")[0].trim();
11804
11872
  let after = (props.after || "").split("#")[0].trim();
@@ -11806,8 +11874,8 @@ var diffDirective = ({ props, slots }) => {
11806
11874
  const urls = [];
11807
11875
  const raw = slots.default || "";
11808
11876
  let m;
11809
- IMG_RE2.lastIndex = 0;
11810
- while ((m = IMG_RE2.exec(raw)) !== null) {
11877
+ IMG_RE.lastIndex = 0;
11878
+ while ((m = IMG_RE.exec(raw)) !== null) {
11811
11879
  urls.push(m[2].split("#")[0].trim());
11812
11880
  }
11813
11881
  if (!before && urls.length > 0) before = urls[0];
@@ -11820,21 +11888,11 @@ var diffDirective = ({ props, slots }) => {
11820
11888
  figure.className = "nr-diff";
11821
11889
  figure.tabIndex = 0;
11822
11890
  figure.setAttribute("aria-label", "Image comparison slider");
11823
- if (props.class) figure.classList.add(...props.class.split(/\s+/).filter(Boolean));
11824
- if (props.style) figure.setAttribute("style", props.style);
11891
+ applyBaseProps(figure, props);
11825
11892
  if (props.aspect) figure.style.aspectRatio = props.aspect;
11826
11893
  if (props.height) figure.style.height = props.height;
11827
11894
  if (props.width) figure.style.width = props.width;
11828
- if (props.float) {
11829
- if (props.float === "left" || props.float === "right") {
11830
- figure.style.float = props.float;
11831
- if (!props.width) figure.style.maxWidth = "50%";
11832
- figure.style.marginInlineStart = props.float === "right" ? "1rem" : "";
11833
- figure.style.marginInlineEnd = props.float === "left" ? "1rem" : "";
11834
- } else if (props.float === "center") {
11835
- figure.style.marginInline = "auto";
11836
- }
11837
- }
11895
+ applyFloatStyle(figure, props.float, props.width);
11838
11896
  const beforeItem = document.createElement("div");
11839
11897
  beforeItem.className = "nr-diff__item nr-diff__item--before";
11840
11898
  beforeItem.setAttribute("role", "img");
@@ -11897,8 +11955,7 @@ var diff_default = diffDirective;
11897
11955
  var hover3dDirective = ({ props, renderSlot }) => {
11898
11956
  const container = document.createElement("div");
11899
11957
  container.className = "nr-hover-3d";
11900
- if (props.class) container.classList.add(...props.class.split(/\s+/).filter(Boolean));
11901
- if (props.style) container.setAttribute("style", props.style);
11958
+ applyBaseProps(container, props);
11902
11959
  const stage = document.createElement("div");
11903
11960
  stage.className = "nr-hover-3d__stage";
11904
11961
  stage.appendChild(renderSlot("default"));
@@ -11911,14 +11968,13 @@ var hover3dDirective = ({ props, renderSlot }) => {
11911
11968
  var hover3d_default = hover3dDirective;
11912
11969
 
11913
11970
  // vanilla/directives/hovergallery.ts
11914
- var IMG_RE3 = /!\[([^\]]*)\]\(([^)]+)\)/g;
11915
11971
  var MAX_IMAGES = 10;
11916
11972
  var hovergalleryDirective = ({ props, slots }) => {
11917
11973
  const images = [];
11918
11974
  const raw = slots.default || "";
11919
11975
  let m;
11920
- IMG_RE3.lastIndex = 0;
11921
- while ((m = IMG_RE3.exec(raw)) !== null) {
11976
+ IMG_RE.lastIndex = 0;
11977
+ while ((m = IMG_RE.exec(raw)) !== null) {
11922
11978
  images.push({ src: m[2].split("#")[0].trim(), alt: m[1].trim() || "gallery image" });
11923
11979
  }
11924
11980
  if (images.length === 0) {
@@ -11927,9 +11983,8 @@ var hovergalleryDirective = ({ props, slots }) => {
11927
11983
  const count = Math.min(images.length, MAX_IMAGES);
11928
11984
  const figure = document.createElement("figure");
11929
11985
  figure.className = "nr-hover-gallery";
11986
+ applyBaseProps(figure, props);
11930
11987
  if (props.aspect) figure.style.aspectRatio = props.aspect;
11931
- if (props.class) figure.classList.add(...props.class.split(/\s+/).filter(Boolean));
11932
- if (props.style) figure.setAttribute("style", (figure.getAttribute("style") || "") + ";" + props.style);
11933
11988
  const imgEls = [];
11934
11989
  for (let i = 0; i < count; i++) {
11935
11990
  const el = document.createElement("img");
@@ -11979,28 +12034,11 @@ var hovergalleryDirective = ({ props, slots }) => {
11979
12034
  var hovergallery_default = hovergalleryDirective;
11980
12035
 
11981
12036
  // vanilla/directives/chat.ts
11982
- var CHAT_THEME_TOKENS = /* @__PURE__ */ new Set([
11983
- "primary",
11984
- "secondary",
11985
- "accent",
11986
- "neutral",
11987
- "info",
11988
- "success",
11989
- "warning",
11990
- "error"
11991
- ]);
11992
- function isArbitraryColor(value) {
11993
- if (CHAT_THEME_TOKENS.has(value)) return false;
11994
- if (/^(#|rgb|hsl|oklch|oklab|lab|lch|color\()/i.test(value)) return true;
11995
- if (/^[a-zA-Z]+$/.test(value)) return true;
11996
- return false;
11997
- }
11998
12037
  var chatItemDirective = ({ props, renderSlot }) => {
11999
12038
  const side = props.side === "end" ? "end" : "start";
12000
12039
  const wrap = document.createElement("div");
12001
12040
  wrap.className = `nr-chat nr-chat--${side}`;
12002
- if (props.class) wrap.classList.add(...props.class.split(/\s+/).filter(Boolean));
12003
- if (props.style) wrap.setAttribute("style", props.style);
12041
+ applyBaseProps(wrap, props);
12004
12042
  const header = document.createElement("div");
12005
12043
  header.className = "nr-chat__header";
12006
12044
  if (props.name) {
@@ -12025,14 +12063,8 @@ var chatItemDirective = ({ props, renderSlot }) => {
12025
12063
  avatar.appendChild(img);
12026
12064
  wrap.appendChild(avatar);
12027
12065
  }
12028
- const isThemeToken = CHAT_THEME_TOKENS.has(props.color || "");
12029
- const colorClass = isThemeToken ? ` nr-chat__bubble--${props.color}` : "";
12030
12066
  const bubble = document.createElement("div");
12031
- bubble.className = `nr-chat__bubble${colorClass}`;
12032
- if (props.color && isArbitraryColor(props.color) && !isThemeToken) {
12033
- bubble.style.background = props.color;
12034
- bubble.style.color = "white";
12035
- }
12067
+ bubble.className = `nr-chat__bubble${applyColor(bubble, props.color, "nr-chat__bubble")}`;
12036
12068
  bubble.appendChild(renderSlot("default"));
12037
12069
  wrap.appendChild(bubble);
12038
12070
  if (props.footer) {
@@ -12046,8 +12078,7 @@ var chatItemDirective = ({ props, renderSlot }) => {
12046
12078
  var chatDirective = ({ props, renderSlot }) => {
12047
12079
  const wrap = document.createElement("div");
12048
12080
  wrap.className = "nr-chat";
12049
- if (props.class) wrap.classList.add(...props.class.split(/\s+/).filter(Boolean));
12050
- if (props.style) wrap.setAttribute("style", props.style);
12081
+ applyBaseProps(wrap, props);
12051
12082
  wrap.appendChild(renderSlot("default"));
12052
12083
  return wrap;
12053
12084
  };
@@ -12083,8 +12114,7 @@ function bindEventProp(el, eventProp) {
12083
12114
  var richlistItemDirective = ({ props, renderSlot }) => {
12084
12115
  const li = document.createElement("li");
12085
12116
  li.className = "nr-richlist__item";
12086
- if (props.class) li.classList.add(...props.class.split(/\s+/).filter(Boolean));
12087
- if (props.style) li.setAttribute("style", props.style);
12117
+ applyBaseProps(li, props);
12088
12118
  if (props.image) {
12089
12119
  const thumb = document.createElement("div");
12090
12120
  thumb.className = "nr-richlist__thumb";
@@ -12146,36 +12176,20 @@ var richlistItemDirective = ({ props, renderSlot }) => {
12146
12176
  var richlistDirective = ({ props, renderSlot }) => {
12147
12177
  const ul = document.createElement("ul");
12148
12178
  ul.className = "nr-richlist";
12149
- if (props.class) ul.classList.add(...props.class.split(/\s+/).filter(Boolean));
12150
- if (props.style) ul.setAttribute("style", props.style);
12179
+ applyBaseProps(ul, props);
12151
12180
  ul.appendChild(renderSlot("default"));
12152
12181
  return ul;
12153
12182
  };
12154
12183
  var richlist_default = richlistDirective;
12155
12184
 
12156
12185
  // vanilla/directives/stat.ts
12157
- var STAT_THEME_TOKENS = /* @__PURE__ */ new Set([
12158
- "primary",
12159
- "secondary",
12160
- "info",
12161
- "success",
12162
- "warning",
12163
- "error"
12164
- ]);
12165
- function isArbitraryColor2(value) {
12166
- if (STAT_THEME_TOKENS.has(value)) return false;
12167
- if (/^(#|rgb|hsl|oklch|oklab|lab|lch|color\()/i.test(value)) return true;
12168
- if (/^[a-zA-Z]+$/.test(value)) return true;
12169
- return false;
12170
- }
12171
12186
  var statDirective = ({ props }) => {
12172
- const isThemeToken = STAT_THEME_TOKENS.has(props.color || "");
12173
- const colorClass = isThemeToken ? ` nr-stat--${props.color}` : "";
12187
+ const statIsThemeToken = isThemeToken(props.color);
12188
+ const colorClass = statIsThemeToken ? ` nr-stat--${props.color}` : "";
12174
12189
  const stat = document.createElement("div");
12175
12190
  stat.className = `nr-stat${colorClass}`;
12176
- if (props.class) stat.classList.add(...props.class.split(/\s+/).filter(Boolean));
12177
- if (props.style) stat.setAttribute("style", props.style);
12178
- const useInlineColor = props.color && isArbitraryColor2(props.color) && !isThemeToken;
12191
+ applyBaseProps(stat, props);
12192
+ const useInlineColor = props.color && isArbitraryColor(props.color) && !statIsThemeToken;
12179
12193
  if (props.icon) {
12180
12194
  const figure = document.createElement("div");
12181
12195
  figure.className = "nr-stat__figure";
@@ -12268,9 +12282,12 @@ function renderHtmlString(html) {
12268
12282
  processedContent = processedContent.replace(
12269
12283
  /<style(?:\s+[^>]*)?>([\s\S]*?)<\/style>/gi,
12270
12284
  (_match, cssContent) => {
12285
+ const trimmed = cssContent.trim();
12286
+ const existing = document.head.querySelector("style[data-nr-global]");
12287
+ if (existing && existing.textContent === trimmed) return "";
12271
12288
  const styleEl = document.createElement("style");
12272
12289
  styleEl.setAttribute("data-nr-global", "");
12273
- styleEl.textContent = cssContent;
12290
+ styleEl.textContent = trimmed;
12274
12291
  document.head.appendChild(styleEl);
12275
12292
  return "";
12276
12293
  }
@@ -12340,19 +12357,14 @@ function renderElement(element, ctx, allElements) {
12340
12357
  switch (element.type) {
12341
12358
  case "header": {
12342
12359
  const tag = `h${element.level}`;
12343
- let text = element.text;
12344
- const alignCenter = text.match(/^->\s*(.+?)\s*<-\s*$/);
12345
- const alignRight = text.match(/^->\s*(.+?)\s*->\s*$/);
12346
- if (alignCenter) text = alignCenter[1];
12347
- else if (alignRight) text = alignRight[1];
12348
12360
  const h = document.createElement(tag);
12349
12361
  h.id = element.id;
12350
12362
  let cls = `md-h${element.level}`;
12351
- if (alignCenter) cls += " text-center";
12352
- if (alignRight) cls += " text-right";
12363
+ if (element.align === "center") cls += " text-center";
12364
+ else if (element.align === "right") cls += " text-right";
12353
12365
  if (element.classes) cls += ` ${element.classes}`;
12354
12366
  h.className = cls;
12355
- h.appendChild(renderInline(text));
12367
+ h.appendChild(renderInline(element.text));
12356
12368
  return h;
12357
12369
  }
12358
12370
  case "paragraph": {