@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.
@@ -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;
@@ -11257,11 +11268,77 @@ function parseInlinePart(part) {
11257
11268
  return document.createTextNode(part);
11258
11269
  }
11259
11270
 
11271
+ // vanilla/utils.ts
11272
+ var THEME_TOKENS = /* @__PURE__ */ new Set([
11273
+ "primary",
11274
+ "secondary",
11275
+ "accent",
11276
+ "neutral",
11277
+ "info",
11278
+ "success",
11279
+ "warning",
11280
+ "error"
11281
+ ]);
11282
+ function isThemeToken(color) {
11283
+ return !!color && THEME_TOKENS.has(color);
11284
+ }
11285
+ function isArbitraryColor(value) {
11286
+ if (THEME_TOKENS.has(value)) return false;
11287
+ if (/^(#|rgb|hsl|oklch|oklab|lab|lch|color\()/i.test(value)) return true;
11288
+ if (/^[a-zA-Z]+$/.test(value)) return true;
11289
+ return false;
11290
+ }
11291
+ function applyBaseProps(el, props) {
11292
+ if (props.class) {
11293
+ el.classList.add(...props.class.split(/\s+/).filter(Boolean));
11294
+ }
11295
+ if (props.style) {
11296
+ const styles = parseCssString(props.style);
11297
+ for (const [key, value] of Object.entries(styles)) {
11298
+ el.style.setProperty(key, String(value));
11299
+ }
11300
+ }
11301
+ }
11302
+ function applyFloatStyle(el, float, width) {
11303
+ if (!float) return;
11304
+ if (float === "left" || float === "right") {
11305
+ el.style.float = float;
11306
+ if (!width) el.style.maxWidth = "50%";
11307
+ el.style.marginInlineStart = float === "right" ? "1rem" : "";
11308
+ el.style.marginInlineEnd = float === "left" ? "1rem" : "";
11309
+ } else if (float === "center") {
11310
+ el.style.marginInline = "auto";
11311
+ }
11312
+ }
11313
+ function applyColor(el, color, classSuffix) {
11314
+ if (!color) return "";
11315
+ if (isThemeToken(color)) {
11316
+ return ` ${classSuffix}--${color}`;
11317
+ }
11318
+ if (isArbitraryColor(color)) {
11319
+ el.style.background = color;
11320
+ el.style.color = "white";
11321
+ }
11322
+ return "";
11323
+ }
11324
+ function openModal(dialog) {
11325
+ if (!dialog.open) {
11326
+ document.body.appendChild(dialog);
11327
+ dialog.showModal();
11328
+ dialog.addEventListener("close", () => dialog.remove(), { once: true });
11329
+ }
11330
+ }
11331
+ var IMG_RE = /!\[([^\]]*)\]\(([^)]+)\)/g;
11332
+ function parseIntProp(value, defaultValue) {
11333
+ if (!value) return defaultValue;
11334
+ const n = parseInt(value, 10);
11335
+ return Number.isNaN(n) ? defaultValue : n;
11336
+ }
11337
+
11260
11338
  // vanilla/directives/admonition.ts
11261
11339
  var admonitionDirective = ({ directiveType, props, renderSlot }) => {
11262
11340
  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);
11341
+ applyBaseProps(el, props);
11265
11342
  const body = el.querySelector(".nr-admonition__body");
11266
11343
  if (body) {
11267
11344
  body.appendChild(renderSlot("default"));
@@ -11277,8 +11354,7 @@ var detailsDirective = ({ props, renderSlot }) => {
11277
11354
  props.icon,
11278
11355
  props.defaultOpen === "true"
11279
11356
  );
11280
- if (props.class) el.classList.add(...props.class.split(/\s+/).filter(Boolean));
11281
- if (props.style) el.setAttribute("style", props.style);
11357
+ applyBaseProps(el, props);
11282
11358
  const body = el.querySelector(".nr-details__body");
11283
11359
  if (body) {
11284
11360
  body.appendChild(renderSlot("default"));
@@ -11291,12 +11367,12 @@ var details_default = detailsDirective;
11291
11367
  var modalDirective = ({ props, renderSlot }) => {
11292
11368
  const label = props.label || props.title || "Open";
11293
11369
  const modalTitle = props.title || "Modal";
11294
- const customClass = props.class || "";
11370
+ const align = props.align || "left";
11295
11371
  const wrapper = document.createElement("div");
11296
- wrapper.className = "nr-modal-trigger";
11372
+ wrapper.className = `nr-modal-trigger${align === "center" ? " nr-modal-trigger--center" : align === "right" ? " nr-modal-trigger--right" : ""}`;
11297
11373
  const btn = document.createElement("button");
11298
11374
  btn.className = `nr-button nr-button--default`;
11299
- if (customClass) btn.classList.add(...customClass.split(/\s+/).filter(Boolean));
11375
+ applyBaseProps(btn, props);
11300
11376
  const icon = props.icon || "open_in_new";
11301
11377
  if (icon) btn.appendChild(createIcon(icon));
11302
11378
  btn.appendChild(document.createTextNode(label));
@@ -11308,15 +11384,7 @@ var modalDirective = ({ props, renderSlot }) => {
11308
11384
  prose.appendChild(renderSlot("default"));
11309
11385
  body.appendChild(prose);
11310
11386
  }
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
- });
11387
+ btn.addEventListener("click", () => openModal(dialog));
11320
11388
  wrapper.appendChild(btn);
11321
11389
  wrapper.appendChild(dialog);
11322
11390
  return wrapper;
@@ -11326,19 +11394,20 @@ var modal_default = modalDirective;
11326
11394
  // vanilla/directives/button.ts
11327
11395
  var buttonDirective = ({ props, renderSlot }) => {
11328
11396
  const url = props.url || props.href || "#";
11329
- const label = props.label;
11397
+ const label = props.label || props.title;
11330
11398
  const icon = props.icon || "near_me";
11331
11399
  const target = props.target || "_blank";
11332
11400
  const customClass = props.class || "";
11401
+ const align = props.align || "left";
11333
11402
  const wrapper = document.createElement("div");
11334
- wrapper.className = "nr-button-wrap";
11403
+ wrapper.className = `nr-button-wrap${align === "center" ? " nr-button-wrap--center" : align === "right" ? " nr-button-wrap--right" : ""}`;
11335
11404
  if (label) {
11336
11405
  const a = document.createElement("a");
11337
11406
  a.href = url;
11338
11407
  a.target = target;
11339
11408
  a.rel = "noopener noreferrer";
11340
11409
  a.className = "nr-button nr-button--default";
11341
- if (customClass) a.classList.add(...customClass.split(/\s+/).filter(Boolean));
11410
+ applyBaseProps(a, props);
11342
11411
  a.appendChild(createIcon(icon));
11343
11412
  a.appendChild(document.createTextNode(label));
11344
11413
  wrapper.appendChild(a);
@@ -11349,7 +11418,7 @@ var buttonDirective = ({ props, renderSlot }) => {
11349
11418
  if (links.length > 0) {
11350
11419
  links.forEach((link) => {
11351
11420
  link.classList.add("nr-button", "nr-button--default");
11352
- if (customClass) link.classList.add(...customClass.split(/\s+/).filter(Boolean));
11421
+ applyBaseProps(link, props);
11353
11422
  });
11354
11423
  wrapper.appendChild(slotContent);
11355
11424
  } else {
@@ -11358,7 +11427,7 @@ var buttonDirective = ({ props, renderSlot }) => {
11358
11427
  a.target = target;
11359
11428
  a.rel = "noopener noreferrer";
11360
11429
  a.className = "nr-button nr-button--default";
11361
- if (customClass) a.classList.add(...customClass.split(/\s+/).filter(Boolean));
11430
+ applyBaseProps(a, props);
11362
11431
  a.appendChild(createIcon(icon));
11363
11432
  a.appendChild(slotContent);
11364
11433
  wrapper.appendChild(a);
@@ -11381,12 +11450,9 @@ var cardDirective = ({
11381
11450
  const { isSingleCard } = options || {};
11382
11451
  const isModal = directiveType === "card-m";
11383
11452
  const isLink = directiveType === "card-b";
11384
- const inlineStyles = props.style ? parseCssString(props.style) : {};
11385
11453
  const card = document.createElement("div");
11386
11454
  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
- }
11455
+ applyBaseProps(card, props);
11390
11456
  if (image) {
11391
11457
  const imgWrap = document.createElement("div");
11392
11458
  imgWrap.className = `nr-card__image${isSingleCard ? " nr-card__image--tall" : ""}`;
@@ -11465,13 +11531,7 @@ var cardDirective = ({
11465
11531
  prose.appendChild(renderSlot("content") || renderSlot("default"));
11466
11532
  modalBody.appendChild(prose);
11467
11533
  }
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
- });
11534
+ card.addEventListener("click", () => openModal(dialog));
11475
11535
  const frag = document.createDocumentFragment();
11476
11536
  frag.appendChild(card);
11477
11537
  frag.appendChild(dialog);
@@ -11519,8 +11579,8 @@ var slideDirective = ({
11519
11579
  if (lines.length === 0) {
11520
11580
  return document.createDocumentFragment();
11521
11581
  }
11522
- const interval = parseInt(props.interval || "3000", 10);
11523
- const speed = parseInt(props.speed || "500", 10);
11582
+ const interval = parseIntProp(props.interval, 3e3);
11583
+ const speed = parseIntProp(props.speed, 500);
11524
11584
  const rawClass = props.class || "";
11525
11585
  const inlineStyle = props.style ? parseCssString(props.style) : {};
11526
11586
  const scopeClass = `sld-${++slideCounter}`;
@@ -11571,10 +11631,11 @@ var slideDirective = ({
11571
11631
  }
11572
11632
  });
11573
11633
  if (lines.length > 1) {
11574
- setInterval(() => {
11634
+ const id = setInterval(() => {
11575
11635
  current = (current + 1) % lines.length;
11576
11636
  track.style.transform = `translateY(${-current * maxH}px)`;
11577
11637
  }, interval);
11638
+ container.dataset.nrIntervalId = String(id);
11578
11639
  }
11579
11640
  return container;
11580
11641
  };
@@ -11584,8 +11645,7 @@ var slide_default = slideDirective;
11584
11645
  var keysDirective = ({ props, slots }) => {
11585
11646
  const wrap = document.createElement("div");
11586
11647
  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);
11648
+ applyBaseProps(wrap, props);
11589
11649
  const sizeClass = props.size ? ` nr-kbd--${props.size}` : "";
11590
11650
  const parts = (slots.default || "").split("+").map((p) => p.trim()).filter(Boolean);
11591
11651
  parts.forEach((part, i) => {
@@ -11609,8 +11669,7 @@ var accordionCounter = 0;
11609
11669
  var accordionItemDirective = ({ props, renderSlot }) => {
11610
11670
  const item = document.createElement("div");
11611
11671
  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);
11672
+ applyBaseProps(item, props);
11614
11673
  const input = document.createElement("input");
11615
11674
  input.type = "radio";
11616
11675
  input.className = "nr-accordion__input";
@@ -11629,8 +11688,7 @@ var accordionItemDirective = ({ props, renderSlot }) => {
11629
11688
  var accordionDirective = ({ props, renderSlot }) => {
11630
11689
  const wrap = document.createElement("div");
11631
11690
  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);
11691
+ applyBaseProps(wrap, props);
11634
11692
  wrap.appendChild(renderSlot("default"));
11635
11693
  const mode = props.mode === "checkbox" ? "checkbox" : "radio";
11636
11694
  const group = `nr-acc-${++accordionCounter}`;
@@ -11643,7 +11701,6 @@ var accordionDirective = ({ props, renderSlot }) => {
11643
11701
  var accordion_default = accordionDirective;
11644
11702
 
11645
11703
  // vanilla/directives/carousel.ts
11646
- var IMG_RE = /!\[([^\]]*)\]\(([^)]+)\)/g;
11647
11704
  var carouselDirective = ({ props, slots }) => {
11648
11705
  const images = [];
11649
11706
  const raw = slots.default || "";
@@ -11659,19 +11716,9 @@ var carouselDirective = ({ props, slots }) => {
11659
11716
  wrap.className = "nr-carousel";
11660
11717
  wrap.tabIndex = 0;
11661
11718
  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);
11719
+ applyBaseProps(wrap, props);
11664
11720
  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
- }
11721
+ applyFloatStyle(wrap, props.float, props.width);
11675
11722
  const viewport = document.createElement("div");
11676
11723
  viewport.className = "nr-carousel__viewport";
11677
11724
  if (props.height) viewport.style.height = props.height;
@@ -11741,11 +11788,10 @@ var DEFAULT_LABELS = ["days", "hours", "min", "sec"];
11741
11788
  var countdownDirective = ({ props }) => {
11742
11789
  const wrap = document.createElement("div");
11743
11790
  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);
11791
+ applyBaseProps(wrap, props);
11746
11792
  const labelParts = (props.labels || "").split("|").map((s) => s.trim());
11747
11793
  const labels = DEFAULT_LABELS.map((label, i) => labelParts[i] || label);
11748
- const digits = parseInt(props.digits || "2", 10);
11794
+ const digits = parseIntProp(props.digits, 2);
11749
11795
  const targetTime = props.target ? new Date(props.target).getTime() : NaN;
11750
11796
  const hasTarget = !Number.isNaN(targetTime);
11751
11797
  const blocks = [];
@@ -11792,13 +11838,15 @@ var countdownDirective = ({ props }) => {
11792
11838
  blocks.push({ value });
11793
11839
  });
11794
11840
  render();
11795
- if (hasTarget) setInterval(render, 1e3);
11841
+ if (hasTarget) {
11842
+ const id = setInterval(render, 1e3);
11843
+ wrap.dataset.nrIntervalId = String(id);
11844
+ }
11796
11845
  return wrap;
11797
11846
  };
11798
11847
  var countdown_default = countdownDirective;
11799
11848
 
11800
11849
  // vanilla/directives/diff.ts
11801
- var IMG_RE2 = /!\[([^\]]*)\]\(([^)]+)\)/g;
11802
11850
  var diffDirective = ({ props, slots }) => {
11803
11851
  let before = (props.before || "").split("#")[0].trim();
11804
11852
  let after = (props.after || "").split("#")[0].trim();
@@ -11806,8 +11854,8 @@ var diffDirective = ({ props, slots }) => {
11806
11854
  const urls = [];
11807
11855
  const raw = slots.default || "";
11808
11856
  let m;
11809
- IMG_RE2.lastIndex = 0;
11810
- while ((m = IMG_RE2.exec(raw)) !== null) {
11857
+ IMG_RE.lastIndex = 0;
11858
+ while ((m = IMG_RE.exec(raw)) !== null) {
11811
11859
  urls.push(m[2].split("#")[0].trim());
11812
11860
  }
11813
11861
  if (!before && urls.length > 0) before = urls[0];
@@ -11820,21 +11868,11 @@ var diffDirective = ({ props, slots }) => {
11820
11868
  figure.className = "nr-diff";
11821
11869
  figure.tabIndex = 0;
11822
11870
  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);
11871
+ applyBaseProps(figure, props);
11825
11872
  if (props.aspect) figure.style.aspectRatio = props.aspect;
11826
11873
  if (props.height) figure.style.height = props.height;
11827
11874
  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
- }
11875
+ applyFloatStyle(figure, props.float, props.width);
11838
11876
  const beforeItem = document.createElement("div");
11839
11877
  beforeItem.className = "nr-diff__item nr-diff__item--before";
11840
11878
  beforeItem.setAttribute("role", "img");
@@ -11897,8 +11935,7 @@ var diff_default = diffDirective;
11897
11935
  var hover3dDirective = ({ props, renderSlot }) => {
11898
11936
  const container = document.createElement("div");
11899
11937
  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);
11938
+ applyBaseProps(container, props);
11902
11939
  const stage = document.createElement("div");
11903
11940
  stage.className = "nr-hover-3d__stage";
11904
11941
  stage.appendChild(renderSlot("default"));
@@ -11911,14 +11948,13 @@ var hover3dDirective = ({ props, renderSlot }) => {
11911
11948
  var hover3d_default = hover3dDirective;
11912
11949
 
11913
11950
  // vanilla/directives/hovergallery.ts
11914
- var IMG_RE3 = /!\[([^\]]*)\]\(([^)]+)\)/g;
11915
11951
  var MAX_IMAGES = 10;
11916
11952
  var hovergalleryDirective = ({ props, slots }) => {
11917
11953
  const images = [];
11918
11954
  const raw = slots.default || "";
11919
11955
  let m;
11920
- IMG_RE3.lastIndex = 0;
11921
- while ((m = IMG_RE3.exec(raw)) !== null) {
11956
+ IMG_RE.lastIndex = 0;
11957
+ while ((m = IMG_RE.exec(raw)) !== null) {
11922
11958
  images.push({ src: m[2].split("#")[0].trim(), alt: m[1].trim() || "gallery image" });
11923
11959
  }
11924
11960
  if (images.length === 0) {
@@ -11927,9 +11963,8 @@ var hovergalleryDirective = ({ props, slots }) => {
11927
11963
  const count = Math.min(images.length, MAX_IMAGES);
11928
11964
  const figure = document.createElement("figure");
11929
11965
  figure.className = "nr-hover-gallery";
11966
+ applyBaseProps(figure, props);
11930
11967
  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
11968
  const imgEls = [];
11934
11969
  for (let i = 0; i < count; i++) {
11935
11970
  const el = document.createElement("img");
@@ -11979,28 +12014,11 @@ var hovergalleryDirective = ({ props, slots }) => {
11979
12014
  var hovergallery_default = hovergalleryDirective;
11980
12015
 
11981
12016
  // 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
12017
  var chatItemDirective = ({ props, renderSlot }) => {
11999
12018
  const side = props.side === "end" ? "end" : "start";
12000
12019
  const wrap = document.createElement("div");
12001
12020
  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);
12021
+ applyBaseProps(wrap, props);
12004
12022
  const header = document.createElement("div");
12005
12023
  header.className = "nr-chat__header";
12006
12024
  if (props.name) {
@@ -12025,14 +12043,8 @@ var chatItemDirective = ({ props, renderSlot }) => {
12025
12043
  avatar.appendChild(img);
12026
12044
  wrap.appendChild(avatar);
12027
12045
  }
12028
- const isThemeToken = CHAT_THEME_TOKENS.has(props.color || "");
12029
- const colorClass = isThemeToken ? ` nr-chat__bubble--${props.color}` : "";
12030
12046
  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
- }
12047
+ bubble.className = `nr-chat__bubble${applyColor(bubble, props.color, "nr-chat__bubble")}`;
12036
12048
  bubble.appendChild(renderSlot("default"));
12037
12049
  wrap.appendChild(bubble);
12038
12050
  if (props.footer) {
@@ -12046,8 +12058,7 @@ var chatItemDirective = ({ props, renderSlot }) => {
12046
12058
  var chatDirective = ({ props, renderSlot }) => {
12047
12059
  const wrap = document.createElement("div");
12048
12060
  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);
12061
+ applyBaseProps(wrap, props);
12051
12062
  wrap.appendChild(renderSlot("default"));
12052
12063
  return wrap;
12053
12064
  };
@@ -12083,8 +12094,7 @@ function bindEventProp(el, eventProp) {
12083
12094
  var richlistItemDirective = ({ props, renderSlot }) => {
12084
12095
  const li = document.createElement("li");
12085
12096
  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);
12097
+ applyBaseProps(li, props);
12088
12098
  if (props.image) {
12089
12099
  const thumb = document.createElement("div");
12090
12100
  thumb.className = "nr-richlist__thumb";
@@ -12146,36 +12156,20 @@ var richlistItemDirective = ({ props, renderSlot }) => {
12146
12156
  var richlistDirective = ({ props, renderSlot }) => {
12147
12157
  const ul = document.createElement("ul");
12148
12158
  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);
12159
+ applyBaseProps(ul, props);
12151
12160
  ul.appendChild(renderSlot("default"));
12152
12161
  return ul;
12153
12162
  };
12154
12163
  var richlist_default = richlistDirective;
12155
12164
 
12156
12165
  // 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
12166
  var statDirective = ({ props }) => {
12172
- const isThemeToken = STAT_THEME_TOKENS.has(props.color || "");
12173
- const colorClass = isThemeToken ? ` nr-stat--${props.color}` : "";
12167
+ const statIsThemeToken = isThemeToken(props.color);
12168
+ const colorClass = statIsThemeToken ? ` nr-stat--${props.color}` : "";
12174
12169
  const stat = document.createElement("div");
12175
12170
  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;
12171
+ applyBaseProps(stat, props);
12172
+ const useInlineColor = props.color && isArbitraryColor(props.color) && !statIsThemeToken;
12179
12173
  if (props.icon) {
12180
12174
  const figure = document.createElement("div");
12181
12175
  figure.className = "nr-stat__figure";
@@ -12268,9 +12262,12 @@ function renderHtmlString(html) {
12268
12262
  processedContent = processedContent.replace(
12269
12263
  /<style(?:\s+[^>]*)?>([\s\S]*?)<\/style>/gi,
12270
12264
  (_match, cssContent) => {
12265
+ const trimmed = cssContent.trim();
12266
+ const existing = document.head.querySelector("style[data-nr-global]");
12267
+ if (existing && existing.textContent === trimmed) return "";
12271
12268
  const styleEl = document.createElement("style");
12272
12269
  styleEl.setAttribute("data-nr-global", "");
12273
- styleEl.textContent = cssContent;
12270
+ styleEl.textContent = trimmed;
12274
12271
  document.head.appendChild(styleEl);
12275
12272
  return "";
12276
12273
  }
@@ -12340,19 +12337,14 @@ function renderElement(element, ctx, allElements) {
12340
12337
  switch (element.type) {
12341
12338
  case "header": {
12342
12339
  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
12340
  const h = document.createElement(tag);
12349
12341
  h.id = element.id;
12350
12342
  let cls = `md-h${element.level}`;
12351
- if (alignCenter) cls += " text-center";
12352
- if (alignRight) cls += " text-right";
12343
+ if (element.align === "center") cls += " text-center";
12344
+ else if (element.align === "right") cls += " text-right";
12353
12345
  if (element.classes) cls += ` ${element.classes}`;
12354
12346
  h.className = cls;
12355
- h.appendChild(renderInline(text));
12347
+ h.appendChild(renderInline(element.text));
12356
12348
  return h;
12357
12349
  }
12358
12350
  case "paragraph": {