@noirmd/previewer 2.1.4 → 2.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -11,6 +11,7 @@ interface HeaderToken {
11
11
  text: string;
12
12
  id: string;
13
13
  classes?: string;
14
+ align?: 'center' | 'right';
14
15
  }
15
16
  interface ParagraphToken {
16
17
  type: 'paragraph';
package/dist/index.d.ts CHANGED
@@ -11,6 +11,7 @@ interface HeaderToken {
11
11
  text: string;
12
12
  id: string;
13
13
  classes?: string;
14
+ align?: 'center' | 'right';
14
15
  }
15
16
  interface ParagraphToken {
16
17
  type: 'paragraph';
package/dist/index.js CHANGED
@@ -1653,6 +1653,22 @@ function parseHtmlAttrs(attrsString) {
1653
1653
  }
1654
1654
 
1655
1655
  // core/parser.ts
1656
+ var VOID_ELEMENTS = /* @__PURE__ */ new Set([
1657
+ "area",
1658
+ "base",
1659
+ "br",
1660
+ "col",
1661
+ "embed",
1662
+ "hr",
1663
+ "img",
1664
+ "input",
1665
+ "link",
1666
+ "meta",
1667
+ "param",
1668
+ "source",
1669
+ "track",
1670
+ "wbr"
1671
+ ]);
1656
1672
  function parseMarkdown(markdown2) {
1657
1673
  if (!markdown2) return [];
1658
1674
  const lines = markdown2.replace(/\r\n/g, "\n").replace(/\r/g, "").split("\n");
@@ -1666,8 +1682,19 @@ function parseMarkdown(markdown2) {
1666
1682
  if (match = trimmed.match(/^(#{1,6})\s+(.+)$/)) {
1667
1683
  const level = match[1].length;
1668
1684
  const rawText = match[2];
1669
- const { text: text2, classes: classes2, id: customId } = extractAttributes(rawText);
1670
- const baseId = customId || generateId(text2.replace(/->|<-/g, ""));
1685
+ const { text: rawParsedText, classes: classes2, id: customId } = extractAttributes(rawText);
1686
+ let text2 = rawParsedText;
1687
+ let align;
1688
+ const alignCenter = text2.match(/^->\s*(.+?)\s*<-\s*$/);
1689
+ const alignRight = text2.match(/^->\s*(.+?)\s*->\s*$/);
1690
+ if (alignCenter) {
1691
+ text2 = alignCenter[1];
1692
+ align = "center";
1693
+ } else if (alignRight) {
1694
+ text2 = alignRight[1];
1695
+ align = "right";
1696
+ }
1697
+ const baseId = customId || generateId(text2);
1671
1698
  let id2 = baseId;
1672
1699
  let n = 1;
1673
1700
  while (usedIds.has(id2)) {
@@ -1675,7 +1702,7 @@ function parseMarkdown(markdown2) {
1675
1702
  id2 = `${baseId}-${n}`;
1676
1703
  }
1677
1704
  usedIds.add(id2);
1678
- result.push({ type: "header", level, text: text2, id: id2, classes: classes2 || void 0 });
1705
+ result.push({ type: "header", level, text: text2, id: id2, classes: classes2 || void 0, align });
1679
1706
  i++;
1680
1707
  continue;
1681
1708
  }
@@ -1851,29 +1878,13 @@ function parseMarkdown(markdown2) {
1851
1878
  let tagStartMatch = trimmed.match(/^<([a-zA-Z][\w-]*)/);
1852
1879
  if (tagStartMatch) {
1853
1880
  const tagName = tagStartMatch[1].toLowerCase();
1854
- const voidElements = /* @__PURE__ */ new Set([
1855
- "area",
1856
- "base",
1857
- "br",
1858
- "col",
1859
- "embed",
1860
- "hr",
1861
- "img",
1862
- "input",
1863
- "link",
1864
- "meta",
1865
- "param",
1866
- "source",
1867
- "track",
1868
- "wbr"
1869
- ]);
1870
1881
  const remainingText = lines.slice(i).join("\n");
1871
1882
  const openTagRegex = new RegExp(`^\\s*<${tagName}\\b([^>]*?)>`, "i");
1872
1883
  const openTagMatch = remainingText.match(openTagRegex);
1873
1884
  if (openTagMatch) {
1874
1885
  const fullOpenTag = openTagMatch[0];
1875
1886
  const attrs = openTagMatch[1].replace(/\s+/g, " ").trim();
1876
- const isSelfClosing = fullOpenTag.endsWith("/>") || voidElements.has(tagName);
1887
+ const isSelfClosing = fullOpenTag.endsWith("/>") || VOID_ELEMENTS.has(tagName);
1877
1888
  if (isSelfClosing) {
1878
1889
  const blockText = remainingText.substring(0, openTagMatch.index + fullOpenTag.length);
1879
1890
  const consumedLines = blockText.split("\n").length;
@@ -11050,6 +11061,7 @@ function createModal(title) {
11050
11061
  header.appendChild(titleEl);
11051
11062
  const closeBtn = document.createElement("button");
11052
11063
  closeBtn.className = "nr-modal__close";
11064
+ closeBtn.setAttribute("aria-label", "Close");
11053
11065
  closeBtn.appendChild(createIcon("close"));
11054
11066
  closeBtn.addEventListener("click", () => dialog.close());
11055
11067
  header.appendChild(closeBtn);
@@ -11270,11 +11282,89 @@ function parseInlinePart(part) {
11270
11282
  return document.createTextNode(part);
11271
11283
  }
11272
11284
 
11285
+ // vanilla/utils.ts
11286
+ var THEME_TOKENS = /* @__PURE__ */ new Set([
11287
+ "primary",
11288
+ "secondary",
11289
+ "accent",
11290
+ "neutral",
11291
+ "info",
11292
+ "success",
11293
+ "warning",
11294
+ "error"
11295
+ ]);
11296
+ function isThemeToken(color) {
11297
+ return !!color && THEME_TOKENS.has(color);
11298
+ }
11299
+ function isArbitraryColor(value) {
11300
+ if (THEME_TOKENS.has(value)) return false;
11301
+ if (/^(#|rgb|hsl|oklch|oklab|lab|lch|color\()/i.test(value)) return true;
11302
+ if (/^[a-zA-Z]+$/.test(value)) return true;
11303
+ return false;
11304
+ }
11305
+ function applyBaseProps(el, props) {
11306
+ if (props.class) {
11307
+ el.classList.add(...props.class.split(/\s+/).filter(Boolean));
11308
+ }
11309
+ if (props.style) {
11310
+ const styles = parseCssString(props.style);
11311
+ for (const [key, value] of Object.entries(styles)) {
11312
+ el.style.setProperty(key, String(value));
11313
+ }
11314
+ }
11315
+ }
11316
+ function applyFloatStyle(el, float, width) {
11317
+ if (!float) return;
11318
+ if (float === "left" || float === "right") {
11319
+ el.style.float = float;
11320
+ if (!width) el.style.maxWidth = "50%";
11321
+ el.style.marginInlineStart = float === "right" ? "1rem" : "";
11322
+ el.style.marginInlineEnd = float === "left" ? "1rem" : "";
11323
+ } else if (float === "center") {
11324
+ el.style.marginInline = "auto";
11325
+ }
11326
+ }
11327
+ function applyColor(el, color, classSuffix) {
11328
+ if (!color) return "";
11329
+ if (isThemeToken(color)) {
11330
+ return ` ${classSuffix}--${color}`;
11331
+ }
11332
+ if (isArbitraryColor(color)) {
11333
+ el.style.background = color;
11334
+ el.style.color = "white";
11335
+ }
11336
+ return "";
11337
+ }
11338
+ function openModal(dialog) {
11339
+ if (!dialog.open) {
11340
+ document.body.appendChild(dialog);
11341
+ dialog.showModal();
11342
+ dialog.addEventListener("close", () => dialog.remove(), { once: true });
11343
+ }
11344
+ }
11345
+ var IMG_RE = /!\[([^\]]*)\]\(([^)]+)\)/g;
11346
+ function applyAlignClass(el, baseClass, align) {
11347
+ if (align === "center") {
11348
+ el.classList.add(`${baseClass}--center`);
11349
+ } else if (align === "right") {
11350
+ el.classList.add(`${baseClass}--right`);
11351
+ }
11352
+ }
11353
+ function resolveIcon(value, fallback) {
11354
+ if (!value) return fallback;
11355
+ if (value === "none" || value === "off") return null;
11356
+ return value;
11357
+ }
11358
+ function parseIntProp(value, defaultValue) {
11359
+ if (!value) return defaultValue;
11360
+ const n = parseInt(value, 10);
11361
+ return Number.isNaN(n) ? defaultValue : n;
11362
+ }
11363
+
11273
11364
  // vanilla/directives/admonition.ts
11274
11365
  var admonitionDirective = ({ directiveType, props, renderSlot }) => {
11275
11366
  const el = createAdmonition(directiveType, props.title, props.icon);
11276
- if (props.class) el.classList.add(...props.class.split(/\s+/).filter(Boolean));
11277
- if (props.style) el.setAttribute("style", props.style);
11367
+ applyBaseProps(el, props);
11278
11368
  const body = el.querySelector(".nr-admonition__body");
11279
11369
  if (body) {
11280
11370
  body.appendChild(renderSlot("default"));
@@ -11290,8 +11380,7 @@ var detailsDirective = ({ props, renderSlot }) => {
11290
11380
  props.icon,
11291
11381
  props.defaultOpen === "true"
11292
11382
  );
11293
- if (props.class) el.classList.add(...props.class.split(/\s+/).filter(Boolean));
11294
- if (props.style) el.setAttribute("style", props.style);
11383
+ applyBaseProps(el, props);
11295
11384
  const body = el.querySelector(".nr-details__body");
11296
11385
  if (body) {
11297
11386
  body.appendChild(renderSlot("default"));
@@ -11304,14 +11393,17 @@ var details_default = detailsDirective;
11304
11393
  var modalDirective = ({ props, renderSlot }) => {
11305
11394
  const label = props.label || props.title || "Open";
11306
11395
  const modalTitle = props.title || "Modal";
11307
- const customClass = props.class || "";
11396
+ const align = props.align || "left";
11397
+ const iconName = resolveIcon(props.icon, "open_in_full");
11308
11398
  const wrapper = document.createElement("div");
11309
11399
  wrapper.className = "nr-modal-trigger";
11400
+ applyAlignClass(wrapper, "nr-modal-trigger", align);
11310
11401
  const btn = document.createElement("button");
11311
- btn.className = `nr-button nr-button--default`;
11312
- if (customClass) btn.classList.add(...customClass.split(/\s+/).filter(Boolean));
11313
- const icon = props.icon || "open_in_new";
11314
- if (icon) btn.appendChild(createIcon(icon));
11402
+ btn.className = "nr-button nr-button--default";
11403
+ btn.setAttribute("aria-haspopup", "dialog");
11404
+ applyColor(btn, props.color, "nr-button");
11405
+ applyBaseProps(btn, props);
11406
+ if (iconName) btn.appendChild(createIcon(iconName));
11315
11407
  btn.appendChild(document.createTextNode(label));
11316
11408
  const dialog = createModal(modalTitle);
11317
11409
  const body = dialog.querySelector(".nr-modal__body");
@@ -11321,15 +11413,7 @@ var modalDirective = ({ props, renderSlot }) => {
11321
11413
  prose.appendChild(renderSlot("default"));
11322
11414
  body.appendChild(prose);
11323
11415
  }
11324
- btn.addEventListener("click", () => {
11325
- if (!dialog.open) {
11326
- document.body.appendChild(dialog);
11327
- dialog.showModal();
11328
- dialog.addEventListener("close", () => {
11329
- dialog.remove();
11330
- }, { once: true });
11331
- }
11332
- });
11416
+ btn.addEventListener("click", () => openModal(dialog));
11333
11417
  wrapper.appendChild(btn);
11334
11418
  wrapper.appendChild(dialog);
11335
11419
  return wrapper;
@@ -11339,20 +11423,22 @@ var modal_default = modalDirective;
11339
11423
  // vanilla/directives/button.ts
11340
11424
  var buttonDirective = ({ props, renderSlot }) => {
11341
11425
  const url = props.url || props.href || "#";
11342
- const label = props.label;
11343
- const icon = props.icon || "near_me";
11426
+ const label = props.label || props.title;
11427
+ const iconName = resolveIcon(props.icon, "touch_app");
11344
11428
  const target = props.target || "_blank";
11345
- const customClass = props.class || "";
11429
+ const align = props.align || "left";
11346
11430
  const wrapper = document.createElement("div");
11347
11431
  wrapper.className = "nr-button-wrap";
11432
+ applyAlignClass(wrapper, "nr-button-wrap", align);
11348
11433
  if (label) {
11349
11434
  const a = document.createElement("a");
11350
11435
  a.href = url;
11351
11436
  a.target = target;
11352
- a.rel = "noopener noreferrer";
11437
+ if (target === "_blank") a.rel = "noopener noreferrer";
11353
11438
  a.className = "nr-button nr-button--default";
11354
- if (customClass) a.classList.add(...customClass.split(/\s+/).filter(Boolean));
11355
- a.appendChild(createIcon(icon));
11439
+ applyColor(a, props.color, "nr-button");
11440
+ applyBaseProps(a, props);
11441
+ if (iconName) a.appendChild(createIcon(iconName));
11356
11442
  a.appendChild(document.createTextNode(label));
11357
11443
  wrapper.appendChild(a);
11358
11444
  return wrapper;
@@ -11362,17 +11448,20 @@ var buttonDirective = ({ props, renderSlot }) => {
11362
11448
  if (links.length > 0) {
11363
11449
  links.forEach((link) => {
11364
11450
  link.classList.add("nr-button", "nr-button--default");
11365
- if (customClass) link.classList.add(...customClass.split(/\s+/).filter(Boolean));
11451
+ applyColor(link, props.color, "nr-button");
11452
+ if (iconName) link.prepend(createIcon(iconName));
11453
+ applyBaseProps(link, props);
11366
11454
  });
11367
11455
  wrapper.appendChild(slotContent);
11368
11456
  } else {
11369
11457
  const a = document.createElement("a");
11370
11458
  a.href = url;
11371
11459
  a.target = target;
11372
- a.rel = "noopener noreferrer";
11460
+ if (target === "_blank") a.rel = "noopener noreferrer";
11373
11461
  a.className = "nr-button nr-button--default";
11374
- if (customClass) a.classList.add(...customClass.split(/\s+/).filter(Boolean));
11375
- a.appendChild(createIcon(icon));
11462
+ applyColor(a, props.color, "nr-button");
11463
+ applyBaseProps(a, props);
11464
+ if (iconName) a.appendChild(createIcon(iconName));
11376
11465
  a.appendChild(slotContent);
11377
11466
  wrapper.appendChild(a);
11378
11467
  }
@@ -11394,12 +11483,9 @@ var cardDirective = ({
11394
11483
  const { isSingleCard } = options || {};
11395
11484
  const isModal = directiveType === "card-m";
11396
11485
  const isLink = directiveType === "card-b";
11397
- const inlineStyles = props.style ? parseCssString(props.style) : {};
11398
11486
  const card = document.createElement("div");
11399
11487
  card.className = `nr-card${isModal || isLink ? " nr-card--interactive" : ""} ${customClass}`.trim();
11400
- for (const [key, value] of Object.entries(inlineStyles)) {
11401
- card.style.setProperty(key, String(value));
11402
- }
11488
+ applyBaseProps(card, props);
11403
11489
  if (image) {
11404
11490
  const imgWrap = document.createElement("div");
11405
11491
  imgWrap.className = `nr-card__image${isSingleCard ? " nr-card__image--tall" : ""}`;
@@ -11478,13 +11564,7 @@ var cardDirective = ({
11478
11564
  prose.appendChild(renderSlot("content") || renderSlot("default"));
11479
11565
  modalBody.appendChild(prose);
11480
11566
  }
11481
- card.addEventListener("click", () => {
11482
- if (!dialog.open) {
11483
- document.body.appendChild(dialog);
11484
- dialog.showModal();
11485
- dialog.addEventListener("close", () => dialog.remove(), { once: true });
11486
- }
11487
- });
11567
+ card.addEventListener("click", () => openModal(dialog));
11488
11568
  const frag = document.createDocumentFragment();
11489
11569
  frag.appendChild(card);
11490
11570
  frag.appendChild(dialog);
@@ -11532,8 +11612,8 @@ var slideDirective = ({
11532
11612
  if (lines.length === 0) {
11533
11613
  return document.createDocumentFragment();
11534
11614
  }
11535
- const interval = parseInt(props.interval || "3000", 10);
11536
- const speed = parseInt(props.speed || "500", 10);
11615
+ const interval = parseIntProp(props.interval, 3e3);
11616
+ const speed = parseIntProp(props.speed, 500);
11537
11617
  const rawClass = props.class || "";
11538
11618
  const inlineStyle = props.style ? parseCssString(props.style) : {};
11539
11619
  const scopeClass = `sld-${++slideCounter}`;
@@ -11584,10 +11664,11 @@ var slideDirective = ({
11584
11664
  }
11585
11665
  });
11586
11666
  if (lines.length > 1) {
11587
- setInterval(() => {
11667
+ const id = setInterval(() => {
11588
11668
  current = (current + 1) % lines.length;
11589
11669
  track.style.transform = `translateY(${-current * maxH}px)`;
11590
11670
  }, interval);
11671
+ container.dataset.nrIntervalId = String(id);
11591
11672
  }
11592
11673
  return container;
11593
11674
  };
@@ -11597,8 +11678,7 @@ var slide_default = slideDirective;
11597
11678
  var keysDirective = ({ props, slots }) => {
11598
11679
  const wrap = document.createElement("div");
11599
11680
  wrap.className = "nr-keys";
11600
- if (props.class) wrap.classList.add(...props.class.split(/\s+/).filter(Boolean));
11601
- if (props.style) wrap.setAttribute("style", props.style);
11681
+ applyBaseProps(wrap, props);
11602
11682
  const sizeClass = props.size ? ` nr-kbd--${props.size}` : "";
11603
11683
  const parts = (slots.default || "").split("+").map((p) => p.trim()).filter(Boolean);
11604
11684
  parts.forEach((part, i) => {
@@ -11622,8 +11702,7 @@ var accordionCounter = 0;
11622
11702
  var accordionItemDirective = ({ props, renderSlot }) => {
11623
11703
  const item = document.createElement("div");
11624
11704
  item.className = "nr-accordion__item";
11625
- if (props.class) item.classList.add(...props.class.split(/\s+/).filter(Boolean));
11626
- if (props.style) item.setAttribute("style", props.style);
11705
+ applyBaseProps(item, props);
11627
11706
  const input = document.createElement("input");
11628
11707
  input.type = "radio";
11629
11708
  input.className = "nr-accordion__input";
@@ -11642,8 +11721,7 @@ var accordionItemDirective = ({ props, renderSlot }) => {
11642
11721
  var accordionDirective = ({ props, renderSlot }) => {
11643
11722
  const wrap = document.createElement("div");
11644
11723
  wrap.className = "nr-accordion";
11645
- if (props.class) wrap.classList.add(...props.class.split(/\s+/).filter(Boolean));
11646
- if (props.style) wrap.setAttribute("style", props.style);
11724
+ applyBaseProps(wrap, props);
11647
11725
  wrap.appendChild(renderSlot("default"));
11648
11726
  const mode = props.mode === "checkbox" ? "checkbox" : "radio";
11649
11727
  const group = `nr-acc-${++accordionCounter}`;
@@ -11656,7 +11734,6 @@ var accordionDirective = ({ props, renderSlot }) => {
11656
11734
  var accordion_default = accordionDirective;
11657
11735
 
11658
11736
  // vanilla/directives/carousel.ts
11659
- var IMG_RE = /!\[([^\]]*)\]\(([^)]+)\)/g;
11660
11737
  var carouselDirective = ({ props, slots }) => {
11661
11738
  const images = [];
11662
11739
  const raw = slots.default || "";
@@ -11672,19 +11749,9 @@ var carouselDirective = ({ props, slots }) => {
11672
11749
  wrap.className = "nr-carousel";
11673
11750
  wrap.tabIndex = 0;
11674
11751
  wrap.setAttribute("aria-label", "Image carousel");
11675
- if (props.class) wrap.classList.add(...props.class.split(/\s+/).filter(Boolean));
11676
- if (props.style) wrap.setAttribute("style", props.style);
11752
+ applyBaseProps(wrap, props);
11677
11753
  if (props.width) wrap.style.width = props.width;
11678
- if (props.float) {
11679
- if (props.float === "left" || props.float === "right") {
11680
- wrap.style.float = props.float;
11681
- if (!props.width) wrap.style.maxWidth = "50%";
11682
- wrap.style.marginInlineStart = props.float === "right" ? "1rem" : "";
11683
- wrap.style.marginInlineEnd = props.float === "left" ? "1rem" : "";
11684
- } else if (props.float === "center") {
11685
- wrap.style.marginInline = "auto";
11686
- }
11687
- }
11754
+ applyFloatStyle(wrap, props.float, props.width);
11688
11755
  const viewport = document.createElement("div");
11689
11756
  viewport.className = "nr-carousel__viewport";
11690
11757
  if (props.height) viewport.style.height = props.height;
@@ -11754,11 +11821,10 @@ var DEFAULT_LABELS = ["days", "hours", "min", "sec"];
11754
11821
  var countdownDirective = ({ props }) => {
11755
11822
  const wrap = document.createElement("div");
11756
11823
  wrap.className = "nr-countdown";
11757
- if (props.class) wrap.classList.add(...props.class.split(/\s+/).filter(Boolean));
11758
- if (props.style) wrap.setAttribute("style", props.style);
11824
+ applyBaseProps(wrap, props);
11759
11825
  const labelParts = (props.labels || "").split("|").map((s) => s.trim());
11760
11826
  const labels = DEFAULT_LABELS.map((label, i) => labelParts[i] || label);
11761
- const digits = parseInt(props.digits || "2", 10);
11827
+ const digits = parseIntProp(props.digits, 2);
11762
11828
  const targetTime = props.target ? new Date(props.target).getTime() : NaN;
11763
11829
  const hasTarget = !Number.isNaN(targetTime);
11764
11830
  const blocks = [];
@@ -11805,13 +11871,15 @@ var countdownDirective = ({ props }) => {
11805
11871
  blocks.push({ value });
11806
11872
  });
11807
11873
  render();
11808
- if (hasTarget) setInterval(render, 1e3);
11874
+ if (hasTarget) {
11875
+ const id = setInterval(render, 1e3);
11876
+ wrap.dataset.nrIntervalId = String(id);
11877
+ }
11809
11878
  return wrap;
11810
11879
  };
11811
11880
  var countdown_default = countdownDirective;
11812
11881
 
11813
11882
  // vanilla/directives/diff.ts
11814
- var IMG_RE2 = /!\[([^\]]*)\]\(([^)]+)\)/g;
11815
11883
  var diffDirective = ({ props, slots }) => {
11816
11884
  let before = (props.before || "").split("#")[0].trim();
11817
11885
  let after = (props.after || "").split("#")[0].trim();
@@ -11819,8 +11887,8 @@ var diffDirective = ({ props, slots }) => {
11819
11887
  const urls = [];
11820
11888
  const raw = slots.default || "";
11821
11889
  let m;
11822
- IMG_RE2.lastIndex = 0;
11823
- while ((m = IMG_RE2.exec(raw)) !== null) {
11890
+ IMG_RE.lastIndex = 0;
11891
+ while ((m = IMG_RE.exec(raw)) !== null) {
11824
11892
  urls.push(m[2].split("#")[0].trim());
11825
11893
  }
11826
11894
  if (!before && urls.length > 0) before = urls[0];
@@ -11833,21 +11901,11 @@ var diffDirective = ({ props, slots }) => {
11833
11901
  figure.className = "nr-diff";
11834
11902
  figure.tabIndex = 0;
11835
11903
  figure.setAttribute("aria-label", "Image comparison slider");
11836
- if (props.class) figure.classList.add(...props.class.split(/\s+/).filter(Boolean));
11837
- if (props.style) figure.setAttribute("style", props.style);
11904
+ applyBaseProps(figure, props);
11838
11905
  if (props.aspect) figure.style.aspectRatio = props.aspect;
11839
11906
  if (props.height) figure.style.height = props.height;
11840
11907
  if (props.width) figure.style.width = props.width;
11841
- if (props.float) {
11842
- if (props.float === "left" || props.float === "right") {
11843
- figure.style.float = props.float;
11844
- if (!props.width) figure.style.maxWidth = "50%";
11845
- figure.style.marginInlineStart = props.float === "right" ? "1rem" : "";
11846
- figure.style.marginInlineEnd = props.float === "left" ? "1rem" : "";
11847
- } else if (props.float === "center") {
11848
- figure.style.marginInline = "auto";
11849
- }
11850
- }
11908
+ applyFloatStyle(figure, props.float, props.width);
11851
11909
  const beforeItem = document.createElement("div");
11852
11910
  beforeItem.className = "nr-diff__item nr-diff__item--before";
11853
11911
  beforeItem.setAttribute("role", "img");
@@ -11910,8 +11968,7 @@ var diff_default = diffDirective;
11910
11968
  var hover3dDirective = ({ props, renderSlot }) => {
11911
11969
  const container = document.createElement("div");
11912
11970
  container.className = "nr-hover-3d";
11913
- if (props.class) container.classList.add(...props.class.split(/\s+/).filter(Boolean));
11914
- if (props.style) container.setAttribute("style", props.style);
11971
+ applyBaseProps(container, props);
11915
11972
  const stage = document.createElement("div");
11916
11973
  stage.className = "nr-hover-3d__stage";
11917
11974
  stage.appendChild(renderSlot("default"));
@@ -11924,14 +11981,13 @@ var hover3dDirective = ({ props, renderSlot }) => {
11924
11981
  var hover3d_default = hover3dDirective;
11925
11982
 
11926
11983
  // vanilla/directives/hovergallery.ts
11927
- var IMG_RE3 = /!\[([^\]]*)\]\(([^)]+)\)/g;
11928
11984
  var MAX_IMAGES = 10;
11929
11985
  var hovergalleryDirective = ({ props, slots }) => {
11930
11986
  const images = [];
11931
11987
  const raw = slots.default || "";
11932
11988
  let m;
11933
- IMG_RE3.lastIndex = 0;
11934
- while ((m = IMG_RE3.exec(raw)) !== null) {
11989
+ IMG_RE.lastIndex = 0;
11990
+ while ((m = IMG_RE.exec(raw)) !== null) {
11935
11991
  images.push({ src: m[2].split("#")[0].trim(), alt: m[1].trim() || "gallery image" });
11936
11992
  }
11937
11993
  if (images.length === 0) {
@@ -11940,9 +11996,8 @@ var hovergalleryDirective = ({ props, slots }) => {
11940
11996
  const count = Math.min(images.length, MAX_IMAGES);
11941
11997
  const figure = document.createElement("figure");
11942
11998
  figure.className = "nr-hover-gallery";
11999
+ applyBaseProps(figure, props);
11943
12000
  if (props.aspect) figure.style.aspectRatio = props.aspect;
11944
- if (props.class) figure.classList.add(...props.class.split(/\s+/).filter(Boolean));
11945
- if (props.style) figure.setAttribute("style", (figure.getAttribute("style") || "") + ";" + props.style);
11946
12001
  const imgEls = [];
11947
12002
  for (let i = 0; i < count; i++) {
11948
12003
  const el = document.createElement("img");
@@ -11992,28 +12047,11 @@ var hovergalleryDirective = ({ props, slots }) => {
11992
12047
  var hovergallery_default = hovergalleryDirective;
11993
12048
 
11994
12049
  // vanilla/directives/chat.ts
11995
- var CHAT_THEME_TOKENS = /* @__PURE__ */ new Set([
11996
- "primary",
11997
- "secondary",
11998
- "accent",
11999
- "neutral",
12000
- "info",
12001
- "success",
12002
- "warning",
12003
- "error"
12004
- ]);
12005
- function isArbitraryColor(value) {
12006
- if (CHAT_THEME_TOKENS.has(value)) return false;
12007
- if (/^(#|rgb|hsl|oklch|oklab|lab|lch|color\()/i.test(value)) return true;
12008
- if (/^[a-zA-Z]+$/.test(value)) return true;
12009
- return false;
12010
- }
12011
12050
  var chatItemDirective = ({ props, renderSlot }) => {
12012
12051
  const side = props.side === "end" ? "end" : "start";
12013
12052
  const wrap = document.createElement("div");
12014
12053
  wrap.className = `nr-chat nr-chat--${side}`;
12015
- if (props.class) wrap.classList.add(...props.class.split(/\s+/).filter(Boolean));
12016
- if (props.style) wrap.setAttribute("style", props.style);
12054
+ applyBaseProps(wrap, props);
12017
12055
  const header = document.createElement("div");
12018
12056
  header.className = "nr-chat__header";
12019
12057
  if (props.name) {
@@ -12038,14 +12076,8 @@ var chatItemDirective = ({ props, renderSlot }) => {
12038
12076
  avatar.appendChild(img);
12039
12077
  wrap.appendChild(avatar);
12040
12078
  }
12041
- const isThemeToken = CHAT_THEME_TOKENS.has(props.color || "");
12042
- const colorClass = isThemeToken ? ` nr-chat__bubble--${props.color}` : "";
12043
12079
  const bubble = document.createElement("div");
12044
- bubble.className = `nr-chat__bubble${colorClass}`;
12045
- if (props.color && isArbitraryColor(props.color) && !isThemeToken) {
12046
- bubble.style.background = props.color;
12047
- bubble.style.color = "white";
12048
- }
12080
+ bubble.className = `nr-chat__bubble${applyColor(bubble, props.color, "nr-chat__bubble")}`;
12049
12081
  bubble.appendChild(renderSlot("default"));
12050
12082
  wrap.appendChild(bubble);
12051
12083
  if (props.footer) {
@@ -12059,8 +12091,7 @@ var chatItemDirective = ({ props, renderSlot }) => {
12059
12091
  var chatDirective = ({ props, renderSlot }) => {
12060
12092
  const wrap = document.createElement("div");
12061
12093
  wrap.className = "nr-chat";
12062
- if (props.class) wrap.classList.add(...props.class.split(/\s+/).filter(Boolean));
12063
- if (props.style) wrap.setAttribute("style", props.style);
12094
+ applyBaseProps(wrap, props);
12064
12095
  wrap.appendChild(renderSlot("default"));
12065
12096
  return wrap;
12066
12097
  };
@@ -12096,8 +12127,7 @@ function bindEventProp(el, eventProp) {
12096
12127
  var richlistItemDirective = ({ props, renderSlot }) => {
12097
12128
  const li = document.createElement("li");
12098
12129
  li.className = "nr-richlist__item";
12099
- if (props.class) li.classList.add(...props.class.split(/\s+/).filter(Boolean));
12100
- if (props.style) li.setAttribute("style", props.style);
12130
+ applyBaseProps(li, props);
12101
12131
  if (props.image) {
12102
12132
  const thumb = document.createElement("div");
12103
12133
  thumb.className = "nr-richlist__thumb";
@@ -12159,36 +12189,20 @@ var richlistItemDirective = ({ props, renderSlot }) => {
12159
12189
  var richlistDirective = ({ props, renderSlot }) => {
12160
12190
  const ul = document.createElement("ul");
12161
12191
  ul.className = "nr-richlist";
12162
- if (props.class) ul.classList.add(...props.class.split(/\s+/).filter(Boolean));
12163
- if (props.style) ul.setAttribute("style", props.style);
12192
+ applyBaseProps(ul, props);
12164
12193
  ul.appendChild(renderSlot("default"));
12165
12194
  return ul;
12166
12195
  };
12167
12196
  var richlist_default = richlistDirective;
12168
12197
 
12169
12198
  // vanilla/directives/stat.ts
12170
- var STAT_THEME_TOKENS = /* @__PURE__ */ new Set([
12171
- "primary",
12172
- "secondary",
12173
- "info",
12174
- "success",
12175
- "warning",
12176
- "error"
12177
- ]);
12178
- function isArbitraryColor2(value) {
12179
- if (STAT_THEME_TOKENS.has(value)) return false;
12180
- if (/^(#|rgb|hsl|oklch|oklab|lab|lch|color\()/i.test(value)) return true;
12181
- if (/^[a-zA-Z]+$/.test(value)) return true;
12182
- return false;
12183
- }
12184
12199
  var statDirective = ({ props }) => {
12185
- const isThemeToken = STAT_THEME_TOKENS.has(props.color || "");
12186
- const colorClass = isThemeToken ? ` nr-stat--${props.color}` : "";
12200
+ const statIsThemeToken = isThemeToken(props.color);
12201
+ const colorClass = statIsThemeToken ? ` nr-stat--${props.color}` : "";
12187
12202
  const stat = document.createElement("div");
12188
12203
  stat.className = `nr-stat${colorClass}`;
12189
- if (props.class) stat.classList.add(...props.class.split(/\s+/).filter(Boolean));
12190
- if (props.style) stat.setAttribute("style", props.style);
12191
- const useInlineColor = props.color && isArbitraryColor2(props.color) && !isThemeToken;
12204
+ applyBaseProps(stat, props);
12205
+ const useInlineColor = props.color && isArbitraryColor(props.color) && !statIsThemeToken;
12192
12206
  if (props.icon) {
12193
12207
  const figure = document.createElement("div");
12194
12208
  figure.className = "nr-stat__figure";
@@ -12281,9 +12295,12 @@ function renderHtmlString(html) {
12281
12295
  processedContent = processedContent.replace(
12282
12296
  /<style(?:\s+[^>]*)?>([\s\S]*?)<\/style>/gi,
12283
12297
  (_match, cssContent) => {
12298
+ const trimmed = cssContent.trim();
12299
+ const existing = document.head.querySelector("style[data-nr-global]");
12300
+ if (existing && existing.textContent === trimmed) return "";
12284
12301
  const styleEl = document.createElement("style");
12285
12302
  styleEl.setAttribute("data-nr-global", "");
12286
- styleEl.textContent = cssContent;
12303
+ styleEl.textContent = trimmed;
12287
12304
  document.head.appendChild(styleEl);
12288
12305
  return "";
12289
12306
  }
@@ -12353,19 +12370,14 @@ function renderElement(element, ctx, allElements) {
12353
12370
  switch (element.type) {
12354
12371
  case "header": {
12355
12372
  const tag = `h${element.level}`;
12356
- let text = element.text;
12357
- const alignCenter = text.match(/^->\s*(.+?)\s*<-\s*$/);
12358
- const alignRight = text.match(/^->\s*(.+?)\s*->\s*$/);
12359
- if (alignCenter) text = alignCenter[1];
12360
- else if (alignRight) text = alignRight[1];
12361
12373
  const h = document.createElement(tag);
12362
12374
  h.id = element.id;
12363
12375
  let cls = `md-h${element.level}`;
12364
- if (alignCenter) cls += " text-center";
12365
- if (alignRight) cls += " text-right";
12376
+ if (element.align === "center") cls += " text-center";
12377
+ else if (element.align === "right") cls += " text-right";
12366
12378
  if (element.classes) cls += ` ${element.classes}`;
12367
12379
  h.className = cls;
12368
- h.appendChild(renderInline(text));
12380
+ h.appendChild(renderInline(element.text));
12369
12381
  return h;
12370
12382
  }
12371
12383
  case "paragraph": {