@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/NReditor.cjs +189 -169
- package/dist/NReditor.cjs.map +1 -1
- package/dist/NReditor.js +189 -169
- package/dist/NReditor.js.map +1 -1
- package/dist/button.css +31 -1
- package/dist/card.css +5 -5
- package/dist/core.cjs +31 -20
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +1 -0
- package/dist/core.d.ts +1 -0
- package/dist/core.js +31 -20
- package/dist/core.js.map +1 -1
- package/dist/hover3d.css +1 -1
- package/dist/index.cjs +178 -166
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +178 -166
- package/dist/index.js.map +1 -1
- package/dist/modal.css +7 -1
- package/dist/react.cjs +189 -169
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +1 -0
- package/dist/react.d.ts +1 -0
- package/dist/react.js +189 -169
- package/dist/react.js.map +1 -1
- package/dist/vanilla.cjs +178 -166
- package/dist/vanilla.cjs.map +1 -1
- package/dist/vanilla.d.cts +1 -0
- package/dist/vanilla.d.ts +1 -0
- package/dist/vanilla.js +178 -166
- package/dist/vanilla.js.map +1 -1
- package/dist/vue.cjs +178 -166
- package/dist/vue.cjs.map +1 -1
- package/dist/vue.d.cts +1 -0
- package/dist/vue.d.ts +1 -0
- package/dist/vue.js +178 -166
- package/dist/vue.js.map +1 -1
- package/package.json +1 -1
package/dist/NReditor.cjs
CHANGED
|
@@ -565,6 +565,22 @@ function parseHtmlAttrs(attrsString) {
|
|
|
565
565
|
}
|
|
566
566
|
|
|
567
567
|
// core/parser.ts
|
|
568
|
+
var VOID_ELEMENTS = /* @__PURE__ */ new Set([
|
|
569
|
+
"area",
|
|
570
|
+
"base",
|
|
571
|
+
"br",
|
|
572
|
+
"col",
|
|
573
|
+
"embed",
|
|
574
|
+
"hr",
|
|
575
|
+
"img",
|
|
576
|
+
"input",
|
|
577
|
+
"link",
|
|
578
|
+
"meta",
|
|
579
|
+
"param",
|
|
580
|
+
"source",
|
|
581
|
+
"track",
|
|
582
|
+
"wbr"
|
|
583
|
+
]);
|
|
568
584
|
function parseMarkdown(markdown2) {
|
|
569
585
|
if (!markdown2) return [];
|
|
570
586
|
const lines = markdown2.replace(/\r\n/g, "\n").replace(/\r/g, "").split("\n");
|
|
@@ -578,8 +594,19 @@ function parseMarkdown(markdown2) {
|
|
|
578
594
|
if (match = trimmed.match(/^(#{1,6})\s+(.+)$/)) {
|
|
579
595
|
const level = match[1].length;
|
|
580
596
|
const rawText = match[2];
|
|
581
|
-
const { text:
|
|
582
|
-
|
|
597
|
+
const { text: rawParsedText, classes: classes2, id: customId } = extractAttributes(rawText);
|
|
598
|
+
let text2 = rawParsedText;
|
|
599
|
+
let align;
|
|
600
|
+
const alignCenter = text2.match(/^->\s*(.+?)\s*<-\s*$/);
|
|
601
|
+
const alignRight = text2.match(/^->\s*(.+?)\s*->\s*$/);
|
|
602
|
+
if (alignCenter) {
|
|
603
|
+
text2 = alignCenter[1];
|
|
604
|
+
align = "center";
|
|
605
|
+
} else if (alignRight) {
|
|
606
|
+
text2 = alignRight[1];
|
|
607
|
+
align = "right";
|
|
608
|
+
}
|
|
609
|
+
const baseId = customId || generateId(text2);
|
|
583
610
|
let id2 = baseId;
|
|
584
611
|
let n = 1;
|
|
585
612
|
while (usedIds.has(id2)) {
|
|
@@ -587,7 +614,7 @@ function parseMarkdown(markdown2) {
|
|
|
587
614
|
id2 = `${baseId}-${n}`;
|
|
588
615
|
}
|
|
589
616
|
usedIds.add(id2);
|
|
590
|
-
result.push({ type: "header", level, text: text2, id: id2, classes: classes2 || void 0 });
|
|
617
|
+
result.push({ type: "header", level, text: text2, id: id2, classes: classes2 || void 0, align });
|
|
591
618
|
i++;
|
|
592
619
|
continue;
|
|
593
620
|
}
|
|
@@ -763,29 +790,13 @@ function parseMarkdown(markdown2) {
|
|
|
763
790
|
let tagStartMatch = trimmed.match(/^<([a-zA-Z][\w-]*)/);
|
|
764
791
|
if (tagStartMatch) {
|
|
765
792
|
const tagName = tagStartMatch[1].toLowerCase();
|
|
766
|
-
const voidElements = /* @__PURE__ */ new Set([
|
|
767
|
-
"area",
|
|
768
|
-
"base",
|
|
769
|
-
"br",
|
|
770
|
-
"col",
|
|
771
|
-
"embed",
|
|
772
|
-
"hr",
|
|
773
|
-
"img",
|
|
774
|
-
"input",
|
|
775
|
-
"link",
|
|
776
|
-
"meta",
|
|
777
|
-
"param",
|
|
778
|
-
"source",
|
|
779
|
-
"track",
|
|
780
|
-
"wbr"
|
|
781
|
-
]);
|
|
782
793
|
const remainingText = lines.slice(i).join("\n");
|
|
783
794
|
const openTagRegex = new RegExp(`^\\s*<${tagName}\\b([^>]*?)>`, "i");
|
|
784
795
|
const openTagMatch = remainingText.match(openTagRegex);
|
|
785
796
|
if (openTagMatch) {
|
|
786
797
|
const fullOpenTag = openTagMatch[0];
|
|
787
798
|
const attrs = openTagMatch[1].replace(/\s+/g, " ").trim();
|
|
788
|
-
const isSelfClosing = fullOpenTag.endsWith("/>") ||
|
|
799
|
+
const isSelfClosing = fullOpenTag.endsWith("/>") || VOID_ELEMENTS.has(tagName);
|
|
789
800
|
if (isSelfClosing) {
|
|
790
801
|
const blockText = remainingText.substring(0, openTagMatch.index + fullOpenTag.length);
|
|
791
802
|
const consumedLines = blockText.split("\n").length;
|
|
@@ -1088,6 +1099,7 @@ function createModal(title) {
|
|
|
1088
1099
|
header.appendChild(titleEl);
|
|
1089
1100
|
const closeBtn = document.createElement("button");
|
|
1090
1101
|
closeBtn.className = "nr-modal__close";
|
|
1102
|
+
closeBtn.setAttribute("aria-label", "Close");
|
|
1091
1103
|
closeBtn.appendChild(createIcon("close"));
|
|
1092
1104
|
closeBtn.addEventListener("click", () => dialog.close());
|
|
1093
1105
|
header.appendChild(closeBtn);
|
|
@@ -1308,11 +1320,89 @@ function parseInlinePart(part) {
|
|
|
1308
1320
|
return document.createTextNode(part);
|
|
1309
1321
|
}
|
|
1310
1322
|
|
|
1323
|
+
// vanilla/utils.ts
|
|
1324
|
+
var THEME_TOKENS = /* @__PURE__ */ new Set([
|
|
1325
|
+
"primary",
|
|
1326
|
+
"secondary",
|
|
1327
|
+
"accent",
|
|
1328
|
+
"neutral",
|
|
1329
|
+
"info",
|
|
1330
|
+
"success",
|
|
1331
|
+
"warning",
|
|
1332
|
+
"error"
|
|
1333
|
+
]);
|
|
1334
|
+
function isThemeToken(color) {
|
|
1335
|
+
return !!color && THEME_TOKENS.has(color);
|
|
1336
|
+
}
|
|
1337
|
+
function isArbitraryColor(value) {
|
|
1338
|
+
if (THEME_TOKENS.has(value)) return false;
|
|
1339
|
+
if (/^(#|rgb|hsl|oklch|oklab|lab|lch|color\()/i.test(value)) return true;
|
|
1340
|
+
if (/^[a-zA-Z]+$/.test(value)) return true;
|
|
1341
|
+
return false;
|
|
1342
|
+
}
|
|
1343
|
+
function applyBaseProps(el, props) {
|
|
1344
|
+
if (props.class) {
|
|
1345
|
+
el.classList.add(...props.class.split(/\s+/).filter(Boolean));
|
|
1346
|
+
}
|
|
1347
|
+
if (props.style) {
|
|
1348
|
+
const styles = parseCssString(props.style);
|
|
1349
|
+
for (const [key, value] of Object.entries(styles)) {
|
|
1350
|
+
el.style.setProperty(key, String(value));
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
function applyFloatStyle(el, float, width) {
|
|
1355
|
+
if (!float) return;
|
|
1356
|
+
if (float === "left" || float === "right") {
|
|
1357
|
+
el.style.float = float;
|
|
1358
|
+
if (!width) el.style.maxWidth = "50%";
|
|
1359
|
+
el.style.marginInlineStart = float === "right" ? "1rem" : "";
|
|
1360
|
+
el.style.marginInlineEnd = float === "left" ? "1rem" : "";
|
|
1361
|
+
} else if (float === "center") {
|
|
1362
|
+
el.style.marginInline = "auto";
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
function applyColor(el, color, classSuffix) {
|
|
1366
|
+
if (!color) return "";
|
|
1367
|
+
if (isThemeToken(color)) {
|
|
1368
|
+
return ` ${classSuffix}--${color}`;
|
|
1369
|
+
}
|
|
1370
|
+
if (isArbitraryColor(color)) {
|
|
1371
|
+
el.style.background = color;
|
|
1372
|
+
el.style.color = "white";
|
|
1373
|
+
}
|
|
1374
|
+
return "";
|
|
1375
|
+
}
|
|
1376
|
+
function openModal(dialog) {
|
|
1377
|
+
if (!dialog.open) {
|
|
1378
|
+
document.body.appendChild(dialog);
|
|
1379
|
+
dialog.showModal();
|
|
1380
|
+
dialog.addEventListener("close", () => dialog.remove(), { once: true });
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
var IMG_RE = /!\[([^\]]*)\]\(([^)]+)\)/g;
|
|
1384
|
+
function applyAlignClass(el, baseClass, align) {
|
|
1385
|
+
if (align === "center") {
|
|
1386
|
+
el.classList.add(`${baseClass}--center`);
|
|
1387
|
+
} else if (align === "right") {
|
|
1388
|
+
el.classList.add(`${baseClass}--right`);
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
function resolveIcon(value, fallback) {
|
|
1392
|
+
if (!value) return fallback;
|
|
1393
|
+
if (value === "none" || value === "off") return null;
|
|
1394
|
+
return value;
|
|
1395
|
+
}
|
|
1396
|
+
function parseIntProp(value, defaultValue) {
|
|
1397
|
+
if (!value) return defaultValue;
|
|
1398
|
+
const n = parseInt(value, 10);
|
|
1399
|
+
return Number.isNaN(n) ? defaultValue : n;
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1311
1402
|
// vanilla/directives/admonition.ts
|
|
1312
1403
|
var admonitionDirective = ({ directiveType, props, renderSlot }) => {
|
|
1313
1404
|
const el = createAdmonition(directiveType, props.title, props.icon);
|
|
1314
|
-
|
|
1315
|
-
if (props.style) el.setAttribute("style", props.style);
|
|
1405
|
+
applyBaseProps(el, props);
|
|
1316
1406
|
const body = el.querySelector(".nr-admonition__body");
|
|
1317
1407
|
if (body) {
|
|
1318
1408
|
body.appendChild(renderSlot("default"));
|
|
@@ -1328,8 +1418,7 @@ var detailsDirective = ({ props, renderSlot }) => {
|
|
|
1328
1418
|
props.icon,
|
|
1329
1419
|
props.defaultOpen === "true"
|
|
1330
1420
|
);
|
|
1331
|
-
|
|
1332
|
-
if (props.style) el.setAttribute("style", props.style);
|
|
1421
|
+
applyBaseProps(el, props);
|
|
1333
1422
|
const body = el.querySelector(".nr-details__body");
|
|
1334
1423
|
if (body) {
|
|
1335
1424
|
body.appendChild(renderSlot("default"));
|
|
@@ -1342,14 +1431,17 @@ var details_default = detailsDirective;
|
|
|
1342
1431
|
var modalDirective = ({ props, renderSlot }) => {
|
|
1343
1432
|
const label = props.label || props.title || "Open";
|
|
1344
1433
|
const modalTitle = props.title || "Modal";
|
|
1345
|
-
const
|
|
1434
|
+
const align = props.align || "left";
|
|
1435
|
+
const iconName = resolveIcon(props.icon, "open_in_full");
|
|
1346
1436
|
const wrapper = document.createElement("div");
|
|
1347
1437
|
wrapper.className = "nr-modal-trigger";
|
|
1438
|
+
applyAlignClass(wrapper, "nr-modal-trigger", align);
|
|
1348
1439
|
const btn = document.createElement("button");
|
|
1349
|
-
btn.className =
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1440
|
+
btn.className = "nr-button nr-button--default";
|
|
1441
|
+
btn.setAttribute("aria-haspopup", "dialog");
|
|
1442
|
+
applyColor(btn, props.color, "nr-button");
|
|
1443
|
+
applyBaseProps(btn, props);
|
|
1444
|
+
if (iconName) btn.appendChild(createIcon(iconName));
|
|
1353
1445
|
btn.appendChild(document.createTextNode(label));
|
|
1354
1446
|
const dialog = createModal(modalTitle);
|
|
1355
1447
|
const body = dialog.querySelector(".nr-modal__body");
|
|
@@ -1359,15 +1451,7 @@ var modalDirective = ({ props, renderSlot }) => {
|
|
|
1359
1451
|
prose.appendChild(renderSlot("default"));
|
|
1360
1452
|
body.appendChild(prose);
|
|
1361
1453
|
}
|
|
1362
|
-
btn.addEventListener("click", () =>
|
|
1363
|
-
if (!dialog.open) {
|
|
1364
|
-
document.body.appendChild(dialog);
|
|
1365
|
-
dialog.showModal();
|
|
1366
|
-
dialog.addEventListener("close", () => {
|
|
1367
|
-
dialog.remove();
|
|
1368
|
-
}, { once: true });
|
|
1369
|
-
}
|
|
1370
|
-
});
|
|
1454
|
+
btn.addEventListener("click", () => openModal(dialog));
|
|
1371
1455
|
wrapper.appendChild(btn);
|
|
1372
1456
|
wrapper.appendChild(dialog);
|
|
1373
1457
|
return wrapper;
|
|
@@ -1377,20 +1461,22 @@ var modal_default = modalDirective;
|
|
|
1377
1461
|
// vanilla/directives/button.ts
|
|
1378
1462
|
var buttonDirective = ({ props, renderSlot }) => {
|
|
1379
1463
|
const url = props.url || props.href || "#";
|
|
1380
|
-
const label = props.label;
|
|
1381
|
-
const
|
|
1464
|
+
const label = props.label || props.title;
|
|
1465
|
+
const iconName = resolveIcon(props.icon, "touch_app");
|
|
1382
1466
|
const target = props.target || "_blank";
|
|
1383
|
-
const
|
|
1467
|
+
const align = props.align || "left";
|
|
1384
1468
|
const wrapper = document.createElement("div");
|
|
1385
1469
|
wrapper.className = "nr-button-wrap";
|
|
1470
|
+
applyAlignClass(wrapper, "nr-button-wrap", align);
|
|
1386
1471
|
if (label) {
|
|
1387
1472
|
const a = document.createElement("a");
|
|
1388
1473
|
a.href = url;
|
|
1389
1474
|
a.target = target;
|
|
1390
|
-
a.rel = "noopener noreferrer";
|
|
1475
|
+
if (target === "_blank") a.rel = "noopener noreferrer";
|
|
1391
1476
|
a.className = "nr-button nr-button--default";
|
|
1392
|
-
|
|
1393
|
-
a
|
|
1477
|
+
applyColor(a, props.color, "nr-button");
|
|
1478
|
+
applyBaseProps(a, props);
|
|
1479
|
+
if (iconName) a.appendChild(createIcon(iconName));
|
|
1394
1480
|
a.appendChild(document.createTextNode(label));
|
|
1395
1481
|
wrapper.appendChild(a);
|
|
1396
1482
|
return wrapper;
|
|
@@ -1400,17 +1486,20 @@ var buttonDirective = ({ props, renderSlot }) => {
|
|
|
1400
1486
|
if (links.length > 0) {
|
|
1401
1487
|
links.forEach((link) => {
|
|
1402
1488
|
link.classList.add("nr-button", "nr-button--default");
|
|
1403
|
-
|
|
1489
|
+
applyColor(link, props.color, "nr-button");
|
|
1490
|
+
if (iconName) link.prepend(createIcon(iconName));
|
|
1491
|
+
applyBaseProps(link, props);
|
|
1404
1492
|
});
|
|
1405
1493
|
wrapper.appendChild(slotContent);
|
|
1406
1494
|
} else {
|
|
1407
1495
|
const a = document.createElement("a");
|
|
1408
1496
|
a.href = url;
|
|
1409
1497
|
a.target = target;
|
|
1410
|
-
a.rel = "noopener noreferrer";
|
|
1498
|
+
if (target === "_blank") a.rel = "noopener noreferrer";
|
|
1411
1499
|
a.className = "nr-button nr-button--default";
|
|
1412
|
-
|
|
1413
|
-
a
|
|
1500
|
+
applyColor(a, props.color, "nr-button");
|
|
1501
|
+
applyBaseProps(a, props);
|
|
1502
|
+
if (iconName) a.appendChild(createIcon(iconName));
|
|
1414
1503
|
a.appendChild(slotContent);
|
|
1415
1504
|
wrapper.appendChild(a);
|
|
1416
1505
|
}
|
|
@@ -1432,12 +1521,9 @@ var cardDirective = ({
|
|
|
1432
1521
|
const { isSingleCard } = options || {};
|
|
1433
1522
|
const isModal = directiveType === "card-m";
|
|
1434
1523
|
const isLink = directiveType === "card-b";
|
|
1435
|
-
const inlineStyles = props.style ? parseCssString(props.style) : {};
|
|
1436
1524
|
const card = document.createElement("div");
|
|
1437
1525
|
card.className = `nr-card${isModal || isLink ? " nr-card--interactive" : ""} ${customClass}`.trim();
|
|
1438
|
-
|
|
1439
|
-
card.style.setProperty(key, String(value));
|
|
1440
|
-
}
|
|
1526
|
+
applyBaseProps(card, props);
|
|
1441
1527
|
if (image) {
|
|
1442
1528
|
const imgWrap = document.createElement("div");
|
|
1443
1529
|
imgWrap.className = `nr-card__image${isSingleCard ? " nr-card__image--tall" : ""}`;
|
|
@@ -1516,13 +1602,7 @@ var cardDirective = ({
|
|
|
1516
1602
|
prose.appendChild(renderSlot("content") || renderSlot("default"));
|
|
1517
1603
|
modalBody.appendChild(prose);
|
|
1518
1604
|
}
|
|
1519
|
-
card.addEventListener("click", () =>
|
|
1520
|
-
if (!dialog.open) {
|
|
1521
|
-
document.body.appendChild(dialog);
|
|
1522
|
-
dialog.showModal();
|
|
1523
|
-
dialog.addEventListener("close", () => dialog.remove(), { once: true });
|
|
1524
|
-
}
|
|
1525
|
-
});
|
|
1605
|
+
card.addEventListener("click", () => openModal(dialog));
|
|
1526
1606
|
const frag = document.createDocumentFragment();
|
|
1527
1607
|
frag.appendChild(card);
|
|
1528
1608
|
frag.appendChild(dialog);
|
|
@@ -1570,8 +1650,8 @@ var slideDirective = ({
|
|
|
1570
1650
|
if (lines.length === 0) {
|
|
1571
1651
|
return document.createDocumentFragment();
|
|
1572
1652
|
}
|
|
1573
|
-
const interval =
|
|
1574
|
-
const speed =
|
|
1653
|
+
const interval = parseIntProp(props.interval, 3e3);
|
|
1654
|
+
const speed = parseIntProp(props.speed, 500);
|
|
1575
1655
|
const rawClass = props.class || "";
|
|
1576
1656
|
const inlineStyle = props.style ? parseCssString(props.style) : {};
|
|
1577
1657
|
const scopeClass = `sld-${++slideCounter}`;
|
|
@@ -1622,10 +1702,11 @@ var slideDirective = ({
|
|
|
1622
1702
|
}
|
|
1623
1703
|
});
|
|
1624
1704
|
if (lines.length > 1) {
|
|
1625
|
-
setInterval(() => {
|
|
1705
|
+
const id = setInterval(() => {
|
|
1626
1706
|
current = (current + 1) % lines.length;
|
|
1627
1707
|
track.style.transform = `translateY(${-current * maxH}px)`;
|
|
1628
1708
|
}, interval);
|
|
1709
|
+
container.dataset.nrIntervalId = String(id);
|
|
1629
1710
|
}
|
|
1630
1711
|
return container;
|
|
1631
1712
|
};
|
|
@@ -1635,8 +1716,7 @@ var slide_default = slideDirective;
|
|
|
1635
1716
|
var keysDirective = ({ props, slots }) => {
|
|
1636
1717
|
const wrap = document.createElement("div");
|
|
1637
1718
|
wrap.className = "nr-keys";
|
|
1638
|
-
|
|
1639
|
-
if (props.style) wrap.setAttribute("style", props.style);
|
|
1719
|
+
applyBaseProps(wrap, props);
|
|
1640
1720
|
const sizeClass = props.size ? ` nr-kbd--${props.size}` : "";
|
|
1641
1721
|
const parts = (slots.default || "").split("+").map((p) => p.trim()).filter(Boolean);
|
|
1642
1722
|
parts.forEach((part, i) => {
|
|
@@ -1660,8 +1740,7 @@ var accordionCounter = 0;
|
|
|
1660
1740
|
var accordionItemDirective = ({ props, renderSlot }) => {
|
|
1661
1741
|
const item = document.createElement("div");
|
|
1662
1742
|
item.className = "nr-accordion__item";
|
|
1663
|
-
|
|
1664
|
-
if (props.style) item.setAttribute("style", props.style);
|
|
1743
|
+
applyBaseProps(item, props);
|
|
1665
1744
|
const input = document.createElement("input");
|
|
1666
1745
|
input.type = "radio";
|
|
1667
1746
|
input.className = "nr-accordion__input";
|
|
@@ -1680,8 +1759,7 @@ var accordionItemDirective = ({ props, renderSlot }) => {
|
|
|
1680
1759
|
var accordionDirective = ({ props, renderSlot }) => {
|
|
1681
1760
|
const wrap = document.createElement("div");
|
|
1682
1761
|
wrap.className = "nr-accordion";
|
|
1683
|
-
|
|
1684
|
-
if (props.style) wrap.setAttribute("style", props.style);
|
|
1762
|
+
applyBaseProps(wrap, props);
|
|
1685
1763
|
wrap.appendChild(renderSlot("default"));
|
|
1686
1764
|
const mode = props.mode === "checkbox" ? "checkbox" : "radio";
|
|
1687
1765
|
const group = `nr-acc-${++accordionCounter}`;
|
|
@@ -1694,7 +1772,6 @@ var accordionDirective = ({ props, renderSlot }) => {
|
|
|
1694
1772
|
var accordion_default = accordionDirective;
|
|
1695
1773
|
|
|
1696
1774
|
// vanilla/directives/carousel.ts
|
|
1697
|
-
var IMG_RE = /!\[([^\]]*)\]\(([^)]+)\)/g;
|
|
1698
1775
|
var carouselDirective = ({ props, slots }) => {
|
|
1699
1776
|
const images = [];
|
|
1700
1777
|
const raw = slots.default || "";
|
|
@@ -1710,19 +1787,9 @@ var carouselDirective = ({ props, slots }) => {
|
|
|
1710
1787
|
wrap.className = "nr-carousel";
|
|
1711
1788
|
wrap.tabIndex = 0;
|
|
1712
1789
|
wrap.setAttribute("aria-label", "Image carousel");
|
|
1713
|
-
|
|
1714
|
-
if (props.style) wrap.setAttribute("style", props.style);
|
|
1790
|
+
applyBaseProps(wrap, props);
|
|
1715
1791
|
if (props.width) wrap.style.width = props.width;
|
|
1716
|
-
|
|
1717
|
-
if (props.float === "left" || props.float === "right") {
|
|
1718
|
-
wrap.style.float = props.float;
|
|
1719
|
-
if (!props.width) wrap.style.maxWidth = "50%";
|
|
1720
|
-
wrap.style.marginInlineStart = props.float === "right" ? "1rem" : "";
|
|
1721
|
-
wrap.style.marginInlineEnd = props.float === "left" ? "1rem" : "";
|
|
1722
|
-
} else if (props.float === "center") {
|
|
1723
|
-
wrap.style.marginInline = "auto";
|
|
1724
|
-
}
|
|
1725
|
-
}
|
|
1792
|
+
applyFloatStyle(wrap, props.float, props.width);
|
|
1726
1793
|
const viewport = document.createElement("div");
|
|
1727
1794
|
viewport.className = "nr-carousel__viewport";
|
|
1728
1795
|
if (props.height) viewport.style.height = props.height;
|
|
@@ -1792,11 +1859,10 @@ var DEFAULT_LABELS = ["days", "hours", "min", "sec"];
|
|
|
1792
1859
|
var countdownDirective = ({ props }) => {
|
|
1793
1860
|
const wrap = document.createElement("div");
|
|
1794
1861
|
wrap.className = "nr-countdown";
|
|
1795
|
-
|
|
1796
|
-
if (props.style) wrap.setAttribute("style", props.style);
|
|
1862
|
+
applyBaseProps(wrap, props);
|
|
1797
1863
|
const labelParts = (props.labels || "").split("|").map((s) => s.trim());
|
|
1798
1864
|
const labels = DEFAULT_LABELS.map((label, i) => labelParts[i] || label);
|
|
1799
|
-
const digits =
|
|
1865
|
+
const digits = parseIntProp(props.digits, 2);
|
|
1800
1866
|
const targetTime = props.target ? new Date(props.target).getTime() : NaN;
|
|
1801
1867
|
const hasTarget = !Number.isNaN(targetTime);
|
|
1802
1868
|
const blocks = [];
|
|
@@ -1843,13 +1909,15 @@ var countdownDirective = ({ props }) => {
|
|
|
1843
1909
|
blocks.push({ value });
|
|
1844
1910
|
});
|
|
1845
1911
|
render();
|
|
1846
|
-
if (hasTarget)
|
|
1912
|
+
if (hasTarget) {
|
|
1913
|
+
const id = setInterval(render, 1e3);
|
|
1914
|
+
wrap.dataset.nrIntervalId = String(id);
|
|
1915
|
+
}
|
|
1847
1916
|
return wrap;
|
|
1848
1917
|
};
|
|
1849
1918
|
var countdown_default = countdownDirective;
|
|
1850
1919
|
|
|
1851
1920
|
// vanilla/directives/diff.ts
|
|
1852
|
-
var IMG_RE2 = /!\[([^\]]*)\]\(([^)]+)\)/g;
|
|
1853
1921
|
var diffDirective = ({ props, slots }) => {
|
|
1854
1922
|
let before = (props.before || "").split("#")[0].trim();
|
|
1855
1923
|
let after = (props.after || "").split("#")[0].trim();
|
|
@@ -1857,8 +1925,8 @@ var diffDirective = ({ props, slots }) => {
|
|
|
1857
1925
|
const urls = [];
|
|
1858
1926
|
const raw = slots.default || "";
|
|
1859
1927
|
let m;
|
|
1860
|
-
|
|
1861
|
-
while ((m =
|
|
1928
|
+
IMG_RE.lastIndex = 0;
|
|
1929
|
+
while ((m = IMG_RE.exec(raw)) !== null) {
|
|
1862
1930
|
urls.push(m[2].split("#")[0].trim());
|
|
1863
1931
|
}
|
|
1864
1932
|
if (!before && urls.length > 0) before = urls[0];
|
|
@@ -1871,21 +1939,11 @@ var diffDirective = ({ props, slots }) => {
|
|
|
1871
1939
|
figure.className = "nr-diff";
|
|
1872
1940
|
figure.tabIndex = 0;
|
|
1873
1941
|
figure.setAttribute("aria-label", "Image comparison slider");
|
|
1874
|
-
|
|
1875
|
-
if (props.style) figure.setAttribute("style", props.style);
|
|
1942
|
+
applyBaseProps(figure, props);
|
|
1876
1943
|
if (props.aspect) figure.style.aspectRatio = props.aspect;
|
|
1877
1944
|
if (props.height) figure.style.height = props.height;
|
|
1878
1945
|
if (props.width) figure.style.width = props.width;
|
|
1879
|
-
|
|
1880
|
-
if (props.float === "left" || props.float === "right") {
|
|
1881
|
-
figure.style.float = props.float;
|
|
1882
|
-
if (!props.width) figure.style.maxWidth = "50%";
|
|
1883
|
-
figure.style.marginInlineStart = props.float === "right" ? "1rem" : "";
|
|
1884
|
-
figure.style.marginInlineEnd = props.float === "left" ? "1rem" : "";
|
|
1885
|
-
} else if (props.float === "center") {
|
|
1886
|
-
figure.style.marginInline = "auto";
|
|
1887
|
-
}
|
|
1888
|
-
}
|
|
1946
|
+
applyFloatStyle(figure, props.float, props.width);
|
|
1889
1947
|
const beforeItem = document.createElement("div");
|
|
1890
1948
|
beforeItem.className = "nr-diff__item nr-diff__item--before";
|
|
1891
1949
|
beforeItem.setAttribute("role", "img");
|
|
@@ -1948,8 +2006,7 @@ var diff_default = diffDirective;
|
|
|
1948
2006
|
var hover3dDirective = ({ props, renderSlot }) => {
|
|
1949
2007
|
const container = document.createElement("div");
|
|
1950
2008
|
container.className = "nr-hover-3d";
|
|
1951
|
-
|
|
1952
|
-
if (props.style) container.setAttribute("style", props.style);
|
|
2009
|
+
applyBaseProps(container, props);
|
|
1953
2010
|
const stage = document.createElement("div");
|
|
1954
2011
|
stage.className = "nr-hover-3d__stage";
|
|
1955
2012
|
stage.appendChild(renderSlot("default"));
|
|
@@ -1962,14 +2019,13 @@ var hover3dDirective = ({ props, renderSlot }) => {
|
|
|
1962
2019
|
var hover3d_default = hover3dDirective;
|
|
1963
2020
|
|
|
1964
2021
|
// vanilla/directives/hovergallery.ts
|
|
1965
|
-
var IMG_RE3 = /!\[([^\]]*)\]\(([^)]+)\)/g;
|
|
1966
2022
|
var MAX_IMAGES = 10;
|
|
1967
2023
|
var hovergalleryDirective = ({ props, slots }) => {
|
|
1968
2024
|
const images = [];
|
|
1969
2025
|
const raw = slots.default || "";
|
|
1970
2026
|
let m;
|
|
1971
|
-
|
|
1972
|
-
while ((m =
|
|
2027
|
+
IMG_RE.lastIndex = 0;
|
|
2028
|
+
while ((m = IMG_RE.exec(raw)) !== null) {
|
|
1973
2029
|
images.push({ src: m[2].split("#")[0].trim(), alt: m[1].trim() || "gallery image" });
|
|
1974
2030
|
}
|
|
1975
2031
|
if (images.length === 0) {
|
|
@@ -1978,9 +2034,8 @@ var hovergalleryDirective = ({ props, slots }) => {
|
|
|
1978
2034
|
const count = Math.min(images.length, MAX_IMAGES);
|
|
1979
2035
|
const figure = document.createElement("figure");
|
|
1980
2036
|
figure.className = "nr-hover-gallery";
|
|
2037
|
+
applyBaseProps(figure, props);
|
|
1981
2038
|
if (props.aspect) figure.style.aspectRatio = props.aspect;
|
|
1982
|
-
if (props.class) figure.classList.add(...props.class.split(/\s+/).filter(Boolean));
|
|
1983
|
-
if (props.style) figure.setAttribute("style", (figure.getAttribute("style") || "") + ";" + props.style);
|
|
1984
2039
|
const imgEls = [];
|
|
1985
2040
|
for (let i = 0; i < count; i++) {
|
|
1986
2041
|
const el = document.createElement("img");
|
|
@@ -2030,28 +2085,11 @@ var hovergalleryDirective = ({ props, slots }) => {
|
|
|
2030
2085
|
var hovergallery_default = hovergalleryDirective;
|
|
2031
2086
|
|
|
2032
2087
|
// vanilla/directives/chat.ts
|
|
2033
|
-
var CHAT_THEME_TOKENS = /* @__PURE__ */ new Set([
|
|
2034
|
-
"primary",
|
|
2035
|
-
"secondary",
|
|
2036
|
-
"accent",
|
|
2037
|
-
"neutral",
|
|
2038
|
-
"info",
|
|
2039
|
-
"success",
|
|
2040
|
-
"warning",
|
|
2041
|
-
"error"
|
|
2042
|
-
]);
|
|
2043
|
-
function isArbitraryColor(value) {
|
|
2044
|
-
if (CHAT_THEME_TOKENS.has(value)) return false;
|
|
2045
|
-
if (/^(#|rgb|hsl|oklch|oklab|lab|lch|color\()/i.test(value)) return true;
|
|
2046
|
-
if (/^[a-zA-Z]+$/.test(value)) return true;
|
|
2047
|
-
return false;
|
|
2048
|
-
}
|
|
2049
2088
|
var chatItemDirective = ({ props, renderSlot }) => {
|
|
2050
2089
|
const side = props.side === "end" ? "end" : "start";
|
|
2051
2090
|
const wrap = document.createElement("div");
|
|
2052
2091
|
wrap.className = `nr-chat nr-chat--${side}`;
|
|
2053
|
-
|
|
2054
|
-
if (props.style) wrap.setAttribute("style", props.style);
|
|
2092
|
+
applyBaseProps(wrap, props);
|
|
2055
2093
|
const header = document.createElement("div");
|
|
2056
2094
|
header.className = "nr-chat__header";
|
|
2057
2095
|
if (props.name) {
|
|
@@ -2076,14 +2114,8 @@ var chatItemDirective = ({ props, renderSlot }) => {
|
|
|
2076
2114
|
avatar.appendChild(img);
|
|
2077
2115
|
wrap.appendChild(avatar);
|
|
2078
2116
|
}
|
|
2079
|
-
const isThemeToken = CHAT_THEME_TOKENS.has(props.color || "");
|
|
2080
|
-
const colorClass = isThemeToken ? ` nr-chat__bubble--${props.color}` : "";
|
|
2081
2117
|
const bubble = document.createElement("div");
|
|
2082
|
-
bubble.className = `nr-chat__bubble${
|
|
2083
|
-
if (props.color && isArbitraryColor(props.color) && !isThemeToken) {
|
|
2084
|
-
bubble.style.background = props.color;
|
|
2085
|
-
bubble.style.color = "white";
|
|
2086
|
-
}
|
|
2118
|
+
bubble.className = `nr-chat__bubble${applyColor(bubble, props.color, "nr-chat__bubble")}`;
|
|
2087
2119
|
bubble.appendChild(renderSlot("default"));
|
|
2088
2120
|
wrap.appendChild(bubble);
|
|
2089
2121
|
if (props.footer) {
|
|
@@ -2097,8 +2129,7 @@ var chatItemDirective = ({ props, renderSlot }) => {
|
|
|
2097
2129
|
var chatDirective = ({ props, renderSlot }) => {
|
|
2098
2130
|
const wrap = document.createElement("div");
|
|
2099
2131
|
wrap.className = "nr-chat";
|
|
2100
|
-
|
|
2101
|
-
if (props.style) wrap.setAttribute("style", props.style);
|
|
2132
|
+
applyBaseProps(wrap, props);
|
|
2102
2133
|
wrap.appendChild(renderSlot("default"));
|
|
2103
2134
|
return wrap;
|
|
2104
2135
|
};
|
|
@@ -2134,8 +2165,7 @@ function bindEventProp(el, eventProp) {
|
|
|
2134
2165
|
var richlistItemDirective = ({ props, renderSlot }) => {
|
|
2135
2166
|
const li = document.createElement("li");
|
|
2136
2167
|
li.className = "nr-richlist__item";
|
|
2137
|
-
|
|
2138
|
-
if (props.style) li.setAttribute("style", props.style);
|
|
2168
|
+
applyBaseProps(li, props);
|
|
2139
2169
|
if (props.image) {
|
|
2140
2170
|
const thumb = document.createElement("div");
|
|
2141
2171
|
thumb.className = "nr-richlist__thumb";
|
|
@@ -2197,36 +2227,20 @@ var richlistItemDirective = ({ props, renderSlot }) => {
|
|
|
2197
2227
|
var richlistDirective = ({ props, renderSlot }) => {
|
|
2198
2228
|
const ul = document.createElement("ul");
|
|
2199
2229
|
ul.className = "nr-richlist";
|
|
2200
|
-
|
|
2201
|
-
if (props.style) ul.setAttribute("style", props.style);
|
|
2230
|
+
applyBaseProps(ul, props);
|
|
2202
2231
|
ul.appendChild(renderSlot("default"));
|
|
2203
2232
|
return ul;
|
|
2204
2233
|
};
|
|
2205
2234
|
var richlist_default = richlistDirective;
|
|
2206
2235
|
|
|
2207
2236
|
// vanilla/directives/stat.ts
|
|
2208
|
-
var STAT_THEME_TOKENS = /* @__PURE__ */ new Set([
|
|
2209
|
-
"primary",
|
|
2210
|
-
"secondary",
|
|
2211
|
-
"info",
|
|
2212
|
-
"success",
|
|
2213
|
-
"warning",
|
|
2214
|
-
"error"
|
|
2215
|
-
]);
|
|
2216
|
-
function isArbitraryColor2(value) {
|
|
2217
|
-
if (STAT_THEME_TOKENS.has(value)) return false;
|
|
2218
|
-
if (/^(#|rgb|hsl|oklch|oklab|lab|lch|color\()/i.test(value)) return true;
|
|
2219
|
-
if (/^[a-zA-Z]+$/.test(value)) return true;
|
|
2220
|
-
return false;
|
|
2221
|
-
}
|
|
2222
2237
|
var statDirective = ({ props }) => {
|
|
2223
|
-
const
|
|
2224
|
-
const colorClass =
|
|
2238
|
+
const statIsThemeToken = isThemeToken(props.color);
|
|
2239
|
+
const colorClass = statIsThemeToken ? ` nr-stat--${props.color}` : "";
|
|
2225
2240
|
const stat = document.createElement("div");
|
|
2226
2241
|
stat.className = `nr-stat${colorClass}`;
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
const useInlineColor = props.color && isArbitraryColor2(props.color) && !isThemeToken;
|
|
2242
|
+
applyBaseProps(stat, props);
|
|
2243
|
+
const useInlineColor = props.color && isArbitraryColor(props.color) && !statIsThemeToken;
|
|
2230
2244
|
if (props.icon) {
|
|
2231
2245
|
const figure = document.createElement("div");
|
|
2232
2246
|
figure.className = "nr-stat__figure";
|
|
@@ -2319,9 +2333,12 @@ function renderHtmlString(html) {
|
|
|
2319
2333
|
processedContent = processedContent.replace(
|
|
2320
2334
|
/<style(?:\s+[^>]*)?>([\s\S]*?)<\/style>/gi,
|
|
2321
2335
|
(_match, cssContent) => {
|
|
2336
|
+
const trimmed = cssContent.trim();
|
|
2337
|
+
const existing = document.head.querySelector("style[data-nr-global]");
|
|
2338
|
+
if (existing && existing.textContent === trimmed) return "";
|
|
2322
2339
|
const styleEl = document.createElement("style");
|
|
2323
2340
|
styleEl.setAttribute("data-nr-global", "");
|
|
2324
|
-
styleEl.textContent =
|
|
2341
|
+
styleEl.textContent = trimmed;
|
|
2325
2342
|
document.head.appendChild(styleEl);
|
|
2326
2343
|
return "";
|
|
2327
2344
|
}
|
|
@@ -2391,19 +2408,14 @@ function renderElement(element, ctx, allElements) {
|
|
|
2391
2408
|
switch (element.type) {
|
|
2392
2409
|
case "header": {
|
|
2393
2410
|
const tag = `h${element.level}`;
|
|
2394
|
-
let text = element.text;
|
|
2395
|
-
const alignCenter = text.match(/^->\s*(.+?)\s*<-\s*$/);
|
|
2396
|
-
const alignRight = text.match(/^->\s*(.+?)\s*->\s*$/);
|
|
2397
|
-
if (alignCenter) text = alignCenter[1];
|
|
2398
|
-
else if (alignRight) text = alignRight[1];
|
|
2399
2411
|
const h = document.createElement(tag);
|
|
2400
2412
|
h.id = element.id;
|
|
2401
2413
|
let cls = `md-h${element.level}`;
|
|
2402
|
-
if (
|
|
2403
|
-
if (
|
|
2414
|
+
if (element.align === "center") cls += " text-center";
|
|
2415
|
+
else if (element.align === "right") cls += " text-right";
|
|
2404
2416
|
if (element.classes) cls += ` ${element.classes}`;
|
|
2405
2417
|
h.className = cls;
|
|
2406
|
-
h.appendChild(renderInline(text));
|
|
2418
|
+
h.appendChild(renderInline(element.text));
|
|
2407
2419
|
return h;
|
|
2408
2420
|
}
|
|
2409
2421
|
case "paragraph": {
|
|
@@ -2555,7 +2567,7 @@ var guideData = [
|
|
|
2555
2567
|
"title": "Introducci\xF3n",
|
|
2556
2568
|
"icon": "menu_book",
|
|
2557
2569
|
"order": 1,
|
|
2558
|
-
"md": '# Introducci\xF3n a NoirMD\n\n**NoirMD** es un editor y motor de renderizado Markdown con extensiones propias: **admoniciones**, **componentes**, **directivas interactivas** y **markdown enriquecido**.\n\nEsta gu\xEDa est\xE1 escrita con el propio motor: cada directiva que ves aqu\xED es una muestra **viva** y funcional, no una captura.\n\n## C\xF3mo usar el editor\n\n| Elemento | Descripci\xF3n |\n| --- | --- |\n| Toolbar superior | Modo (editor / split / preview), guardar, copiar, imprimir, tema, gu\xEDa y configurar |\n| Panel izquierdo | Editor de c\xF3digo con resaltado de sintaxis |\n| Panel derecho | Preview en vivo (en modo split o preview) |\n| Atajo | `Ctrl+S` guarda el contenido |\n\n## Sintaxis de una directiva\n\nLas directivas se escriben con tres dos puntos `:::` y un nombre, opcionalmente con atributos entre llaves:\n\n```\n:::card {title="Mi tarjeta" icon="star"}\n\nContenido **markdown** aqu\xED dentro.\n\n:::\n```\n\nTodo lo que est\xE1 entre la apertura y el cierre `:::` se renderiza con el mismo motor, as\xED que puedes **anidar** directivas.\n\n## Cheatsheet r\xE1pido\n\n| Sintaxis | Resultado |\n| --- | --- |\n| `# T\xEDtulo` \u2192 `###### T\xEDtulo` | Encabezados |\n| `**negrita**` \xB7
|
|
2570
|
+
"md": '# Introducci\xF3n a NoirMD\n\n**NoirMD** es un editor y motor de renderizado Markdown con extensiones propias: **admoniciones**, **componentes**, **directivas interactivas** y **markdown enriquecido**.\n\nEsta gu\xEDa est\xE1 escrita con el propio motor: cada directiva que ves aqu\xED es una muestra **viva** y funcional, no una captura.\n\n## C\xF3mo usar el editor\n\n| Elemento | Descripci\xF3n |\n| --- | --- |\n| Toolbar superior | Modo (editor / split / preview), guardar, copiar, imprimir, tema, gu\xEDa y configurar |\n| Panel izquierdo | Editor de c\xF3digo con resaltado de sintaxis |\n| Panel derecho | Preview en vivo (en modo split o preview) |\n| Atajo | `Ctrl+S` guarda el contenido |\n\n## Sintaxis de una directiva\n\nLas directivas se escriben con tres dos puntos `:::` y un nombre, opcionalmente con atributos entre llaves:\n\n```\n:::card {title="Mi tarjeta" icon="star"}\n\nContenido **markdown** aqu\xED dentro.\n\n:::\n```\n\nTodo lo que est\xE1 entre la apertura y el cierre `:::` se renderiza con el mismo motor, as\xED que puedes **anidar** directivas.\n\n## Cheatsheet r\xE1pido\n\n| Sintaxis | Resultado |\n| --- | --- |\n| `# T\xEDtulo` \u2192 `###### T\xEDtulo` | Encabezados |\n| `**negrita**` \xB7 `_cursiva_` \xB7 `~~tachado~~` | \xC9nfasis |\n| `` `c\xF3digo` `` | C\xF3digo inline |\n| `` ```js `` | Bloque de c\xF3digo con resaltado |\n| `[texto](url)` | Enlace |\n| `` | Imagen |\n| `` | Imagen flotante a la izquierda |\n| `==resaltado==` | Resaltado |\n| `%color%texto%%` | Texto de color |\n| `->centrado<-` | Texto centrado |\n| `!>spoiler<!` | Spoiler oculto |\n| `|[[icono]]|` | Icono Material |\n| `[TOC]` | \xCDndice de contenidos |\n| `:::note` `:::warning` `:::danger` `:::info` `:::greentext` | Admoniciones |\n| `:::card` `:::accordion` `:::carousel` `:::diff` `:::chat` `:::stat` `:::countdown` `:::keys` `:::hover-3d` `:::hover-gallery` `:::richlist` | Componentes |\n| `:::details` `:::modal` `:::button` `:::slide` | Interactivos |\n| `<style>` HTML inline | Bloques HTML (CSS global, HTML crudo) |\n\n## Organizaci\xF3n de la gu\xEDa\n\n- **Markdown** \u2014 sintaxis base y enriquecida (t\xEDtulos, \xE9nfasis, tablas, c\xF3digo, im\xE1genes, inline).\n- **Admoniciones** \u2014 cajas de aviso: nota, warning, danger, info y greentext.\n- **Componentes** \u2014 los 10 componentes de tarjeta, teclas, acorde\xF3n, carrusel, etc.\n- **Interactivos** \u2014 details, modal, botones y slides.\n- **Layout** \u2014 bloques HTML crudo: CSS global con `<style>` e HTML inline.\n\nCada p\xE1gina incluye: la sintaxis exacta, la tabla de props, un ejemplo en vivo y el c\xF3digo fuente para copiar.'
|
|
2559
2571
|
},
|
|
2560
2572
|
{
|
|
2561
2573
|
"id": "titulos",
|
|
@@ -2771,7 +2783,7 @@ var guideData = [
|
|
|
2771
2783
|
"title": "Modal",
|
|
2772
2784
|
"icon": "open_in_full",
|
|
2773
2785
|
"order": 2,
|
|
2774
|
-
"md": '# Modal\n\nLa directiva `:::modal` crea un **di\xE1logo modal** con su bot\xF3n de apertura.\n\n## Sintaxis\n\n```md\n:::modal {title="Confirmar borrado" label="Abrir modal" icon="delete"}\n\xBFSeguro que quieres borrar este documento? Esta acci\xF3n no se puede deshacer.\n\n| Acci\xF3n | Efecto |\n| --- | --- |\n| Aceptar | Borra el documento |\n| Cancelar | No hace nada |\n:::\n```\n\n:::modal {title="Confirmar borrado" label="Abrir modal" icon="delete"}\n\xBFSeguro que quieres borrar este documento? Esta acci\xF3n no se puede deshacer.\n\n| Acci\xF3n | Efecto |\n| --- | --- |\n| Aceptar | Borra el documento |\n| Cancelar | No hace nada |\n:::\n\n## Contenido enriquecido\n\n```md\n:::modal {title="Notas de la versi\xF3n" label="Ver novedades" icon="new_releases"}\n**v2.0** \u2014 cambios principales:\n\n- Nuevo componente `:::diff`\n- Gu\xEDa integrada en el editor\n- Especificidad CSS corregida en im\xE1genes\n:::\n```\n\n:::modal {title="Notas de la versi\xF3n" label="Ver novedades" icon="new_releases"}\n**v2.0** \u2014 cambios principales:\n\n- Nuevo componente `:::diff`\n- Gu\xEDa integrada en el editor\n- Especificidad CSS corregida en im\xE1genes\n:::\n\n## Props\n\n| Prop | Tipo | Descripci\xF3n |\n| --- | --- | --- |\n| `title` | texto | T\xEDtulo del modal |\n| `label` | texto | Texto del bot\xF3n de apertura (
|
|
2786
|
+
"md": '# Modal\n\nLa directiva `:::modal` crea un **di\xE1logo modal** con su bot\xF3n de apertura.\n\n## Sintaxis\n\n```md\n:::modal {title="Confirmar borrado" label="Abrir modal" icon="delete"}\n\xBFSeguro que quieres borrar este documento? Esta acci\xF3n no se puede deshacer.\n\n| Acci\xF3n | Efecto |\n| --- | --- |\n| Aceptar | Borra el documento |\n| Cancelar | No hace nada |\n:::\n```\n\n:::modal {title="Confirmar borrado" label="Abrir modal" icon="delete"}\n\xBFSeguro que quieres borrar este documento? Esta acci\xF3n no se puede deshacer.\n\n| Acci\xF3n | Efecto |\n| --- | --- |\n| Aceptar | Borra el documento |\n| Cancelar | No hace nada |\n:::\n\n## Contenido enriquecido\n\n```md\n:::modal {title="Notas de la versi\xF3n" label="Ver novedades" icon="new_releases"}\n**v2.0** \u2014 cambios principales:\n\n- Nuevo componente `:::diff`\n- Gu\xEDa integrada en el editor\n- Especificidad CSS corregida en im\xE1genes\n:::\n```\n\n:::modal {title="Notas de la versi\xF3n" label="Ver novedades" icon="new_releases"}\n**v2.0** \u2014 cambios principales:\n\n- Nuevo componente `:::diff`\n- Gu\xEDa integrada en el editor\n- Especificidad CSS corregida en im\xE1genes\n:::\n\n## Sin icono\n\nPara ocultar el icono del bot\xF3n de apertura, usa `icon="none"`:\n\n```md\n:::modal {title="Sin icono" label="Abrir" icon="none"}\nContenido del modal.\n:::\n```\n\n:::modal {title="Sin icono" label="Abrir" icon="none"}\nContenido del modal.\n:::\n\n## Color\n\nEl bot\xF3n de apertura acepta tokens de tema (`primary`, `secondary`, `info`, `success`, `warning`, `error`) o colores CSS arbitrarios:\n\n```md\n:::modal {title="\xC9xito" label="Abrir" icon="check_circle" color="success"}\nAcci\xF3n completada correctamente.\n:::\n```\n\n:::modal {title="\xC9xito" label="Abrir" icon="check_circle" color="success"}\nAcci\xF3n completada correctamente.\n:::\n\n## Alineaci\xF3n del bot\xF3n\n\nEl bot\xF3n de apertura se alinea a la izquierda por defecto. Usa el prop `align` para cambiar la alineaci\xF3n:\n\n### Centrado\n\n```md\n:::modal {title="Centrado" label="Abrir" icon="open_in_full" align="center"}\nContenido del modal centrado.\n:::\n```\n\n:::modal {title="Centrado" label="Abrir" icon="open_in_full" align="center"}\nContenido del modal centrado.\n:::\n\n### Alineado a la derecha\n\n```md\n:::modal {title="Derecha" label="Abrir" icon="open_in_full" align="right"}\nContenido del modal alineado a la derecha.\n:::\n```\n\n:::modal {title="Derecha" label="Abrir" icon="open_in_full" align="right"}\nContenido del modal alineado a la derecha.\n:::\n\n## Props\n\n| Prop | Tipo | Descripci\xF3n |\n| --- | --- | --- |\n| `title` | texto | T\xEDtulo del modal |\n| `label` (o `title`) | texto | Texto del bot\xF3n de apertura (`title` funciona como alias, default: \xABOpen\xBB) |\n| `icon` | nombre Material | Icono del bot\xF3n (default `open_in_full`). Usa `icon="none"` para ocultar |\n| `color` | token de tema o CSS | Color del bot\xF3n de apertura |\n| `align` | `left` / `center` / `right` | Alineaci\xF3n del bot\xF3n de apertura (default `left`) |\n| `class` | texto | Clases CSS adicionales (se aplican al bot\xF3n de apertura) |\n| `style` | CSS | Estilos inline (se aplican al bot\xF3n de apertura) |\n\n## Interacci\xF3n\n\n- **Bot\xF3n**: abre el modal (focus se mueve dentro).\n- **Overlay** o bot\xF3n **\xD7**: cierra.\n- **Esc**: cierra (en desktop).\n- `dialog` nativo \u2192 accesible por defecto, focus trapped y `inert` al fondo.'
|
|
2775
2787
|
},
|
|
2776
2788
|
{
|
|
2777
2789
|
"id": "button",
|
|
@@ -2779,7 +2791,7 @@ var guideData = [
|
|
|
2779
2791
|
"title": "Button",
|
|
2780
2792
|
"icon": "touch_app",
|
|
2781
2793
|
"order": 3,
|
|
2782
|
-
"md": '# Button\n\nLa directiva `:::button` crea un **bot\xF3n con enlace** (se abre en pesta\xF1a nueva por defecto).\n\n## Sintaxis\n\n```md\n:::button {label="Documentaci\xF3n" url="https://example.com" icon="menu_book"}\n:::\n```\n\n:::button {label="Documentaci\xF3n" url="https://example.com" icon="menu_book"}\n:::\n\n## Variante con enlace interno\n\n```md\n:::button {label="Ir a la p\xE1gina de notas" url="#admonici\xF3n-nota" icon="sticky_note_2" target="_self"}\n:::\n```\n\n:::button {label="Ir a la p\xE1gina de notas" url="#admonici\xF3n-nota" icon="sticky_note_2" target="_self"}\n:::\n\n## Con contenido markdown\n\nSi el bloque contiene texto/enlaces, se renderizan dentro del bot\xF3n:\n\n```md\n:::button {label="Descargar" url="https://example.com/download" icon="download"}\nDescarga el **manual** en PDF\n:::\n```\n\n:::button {label="Descargar" url="https://example.com/download" icon="download"}\nDescarga el **manual** en PDF\n:::\n\n## Props\n\n| Prop | Tipo | Descripci\xF3n |\n| --- | --- | --- |\n| `label` | texto | Texto del bot\xF3n |\n| `url` (o `href`) | URL | Destino del enlace (default `#`) |\n| `icon` | nombre Material | Icono (default `
|
|
2794
|
+
"md": '# Button\n\nLa directiva `:::button` crea un **bot\xF3n con enlace** (se abre en pesta\xF1a nueva por defecto).\n\n## Sintaxis\n\n```md\n:::button {label="Documentaci\xF3n" url="https://example.com" icon="menu_book"}\n:::\n```\n\n:::button {label="Documentaci\xF3n" url="https://example.com" icon="menu_book"}\n:::\n\n## Variante con enlace interno\n\n```md\n:::button {label="Ir a la p\xE1gina de notas" url="#admonici\xF3n-nota" icon="sticky_note_2" target="_self"}\n:::\n```\n\n:::button {label="Ir a la p\xE1gina de notas" url="#admonici\xF3n-nota" icon="sticky_note_2" target="_self"}\n:::\n\n## Con contenido markdown\n\nSi el bloque contiene texto/enlaces, se renderizan dentro del bot\xF3n. El icono se a\xF1ade autom\xE1ticamente a cada enlace del contenido:\n\n```md\n:::button {label="Descargar" url="https://example.com/download" icon="download"}\nDescarga el **manual** en PDF\n:::\n```\n\n:::button {label="Descargar" url="https://example.com/download" icon="download"}\nDescarga el **manual** en PDF\n:::\n\n## Con enlace en el contenido\n\nSi el contenido es un enlace markdown, el bot\xF3n usa el enlace del contenido y el icono se a\xF1ade al principio:\n\n```md\n:::button {icon="star"}\n[Descarga con FDM](https://example.com/download)\n:::\n```\n\n:::button {icon="star"}\n[Descarga con FDM](https://example.com/download)\n:::\n\n## Sin icono\n\nPara ocultar el icono, usa `icon="none"`:\n\n```md\n:::button {label="Sin icono" url="https://example.com" icon="none"}\n:::\n```\n\n:::button {label="Sin icono" url="https://example.com" icon="none"}\n:::\n\n## Color\n\nLos botones aceptan tokens de tema (`primary`, `secondary`, `info`, `success`, `warning`, `error`) o colores CSS arbitrarios (`red`, `#ff0000`, `rgb(255,0,0)`):\n\n```md\n:::button {label="\xC9xito" url="https://example.com" icon="check_circle" color="success"}\n:::\n```\n\n:::button {label="\xC9xito" url="https://example.com" icon="check_circle" color="success"}\n:::\n\n```md\n:::button {label="Rojo" url="https://example.com" icon="error" color="#e11d48"}\n:::\n```\n\n:::button {label="Rojo" url="https://example.com" icon="error" color="#e11d48"}\n:::\n\n## Alineaci\xF3n\n\nLos botones se alinean a la izquierda por defecto. Usa el prop `align` para cambiar la alineaci\xF3n:\n\n### Centrado\n\n```md\n:::button {label="Centrado" url="https://example.com" icon="center_focus_strong" align="center"}\n:::\n```\n\n:::button {label="Centrado" url="https://example.com" icon="center_focus_strong" align="center"}\n:::\n\n### Alineado a la derecha\n\n```md\n:::button {label="Derecha" url="https://example.com" icon="arrow_forward" align="right"}\n:::\n```\n\n:::button {label="Derecha" url="https://example.com" icon="arrow_forward" align="right"}\n:::\n\n## Props\n\n| Prop | Tipo | Descripci\xF3n |\n| --- | --- | --- |\n| `label` (o `title`) | texto | Texto del bot\xF3n (`title` funciona como alias por compatibilidad) |\n| `url` (o `href`) | URL | Destino del enlace (default `#`) |\n| `icon` | nombre Material | Icono (default `touch_app`). Usa `icon="none"` para ocultar |\n| `target` | `_blank` / `_self` / ... | Destino del enlace (default `_blank`) |\n| `color` | token de tema o CSS | Color del bot\xF3n (ver colores soportados arriba) |\n| `align` | `left` / `center` / `right` | Alineaci\xF3n del bot\xF3n (default `left`) |\n| `class` | texto | Clases CSS adicionales |\n| `style` | CSS | Estilos inline (ej: `style="font-size:1rem"`) |'
|
|
2783
2795
|
},
|
|
2784
2796
|
{
|
|
2785
2797
|
"id": "slide",
|
|
@@ -2796,6 +2808,14 @@ var guideData = [
|
|
|
2796
2808
|
"icon": "code_off",
|
|
2797
2809
|
"order": 1,
|
|
2798
2810
|
"md": '# Bloques HTML\r\n\r\nNoirMD permite escribir **HTML crudo** directamente en el markdown. Seg\xFAn la etiqueta, el comportamiento var\xEDa.\r\n\r\n## CSS global con `<style>`\r\n\r\nEscribe un bloque `<style>` para inyectar CSS en el documento. El contenido se extrae autom\xE1ticamente y se inserta en el `<head>` del DOM.\r\n\r\n```md\r\n<style>\r\n.mi-clase {\r\n background: #f1f5f9;\r\n border-radius: 10px;\r\n padding: 1rem;\r\n}\r\n</style>\r\n```\r\n\r\n<style>\r\n.nr-demo-box {\r\n display: grid;\r\n grid-template-columns: 1fr 1fr;\r\n gap: 1rem;\r\n padding: 1rem;\r\n border-radius: 12px;\r\n background: color-mix(in srgb, var(--color-accent-primary, #0ea5e9) 10%, transparent);\r\n}\r\n.nr-demo-box > div {\r\n padding: 1rem;\r\n border-radius: 8px;\r\n background: var(--color-background-secondary-solid, #1e293b);\r\n}\r\n</style>\r\n\r\n<div class="nr-demo-box">\r\n<div>**A**</div>\r\n<div>**B**</div>\r\n</div>\r\n\r\n> El CSS se aplica al **documento renderizado completo**, no solo al bloque. Define clases una vez y \xFAsalas despu\xE9s en cualquier etiqueta HTML.\r\n\r\n## HTML inline\r\n\r\nCualquier etiqueta HTML escrita directamente en el markdown se renderiza sin procesar como markdown. El contenido dentro se preserva tal cual.\r\n\r\n```md\r\n<div style="text-align: center; padding: 1rem; border: 1px solid #334155; border-radius: 10px;">\r\n HTML escrito a mano funciona tal cual.\r\n</div>\r\n```\r\n\r\n<div style="text-align: center; padding: 1rem; border: 1px solid #334155; border-radius: 10px;">\r\n HTML escrito a mano funciona tal cual.\r\n</div>\r\n\r\n## Elementos interactivos nativos\r\n\r\n```md\r\n<details class="nr-details">\r\n <summary>Detalle nativo con <b>HTML</b></summary>\r\n <p>Los atributos, estilos y eventos se conservan intactos.</p>\r\n</details>\r\n```\r\n\r\n<details class="nr-details">\r\n <summary>Detalle nativo con <b>HTML</b></summary>\r\n <p>Los atributos, estilos y eventos se conservan intactos.</p>\r\n</details>\r\n\r\n## Scripts\r\n\r\nLos bloques `<script>` se ejecutan autom\xE1ticamente al renderizar:\r\n\r\n```md\r\n<script>\r\n console.log(\'Este script se ejecuta al renderizar\');\r\n</script>\r\n```\r\n\r\n> \u26A0\uFE0F Al ser HTML y scripts sin filtrar, \xFAsalos solo con contenido de confianza.\r\n\r\n## Cu\xE1ndo usar bloques HTML\r\n\r\n- Insertar embeds (`iframe`, `video`, widgets externos).\r\n- Estructuras que el markdown no cubre (layouts complejos, formularios nativos).\r\n- Inyectar CSS reutilizable con `<style>`.\r\n- Prototipar HTML antes de convertirlo a directiva.'
|
|
2811
|
+
},
|
|
2812
|
+
{
|
|
2813
|
+
"id": "wrapper-directives",
|
|
2814
|
+
"category": "Layout",
|
|
2815
|
+
"title": "Wrapper Directives",
|
|
2816
|
+
"icon": "crop_free",
|
|
2817
|
+
"order": 1,
|
|
2818
|
+
"md": '# Wrapper Directives\r\n\r\nLas directivas `:::div`, `:::style`, `:::custom` y `:::raw` son wrappers gen\xE9ricos para **envolver contenido** con clases, estilos y atributos personalizados.\r\n\r\n## `:::div` \u2014 Div gen\xE9rico\r\n\r\nEnvuelve contenido en un `<div>` con clases, id, estilos y atributos `data-*`.\r\n\r\n```md\r\n:::div {class="mi-clase" id="seccion" style="padding: 2rem; background: #f0f0f0"}\r\n\r\nContenido **markdown** aqu\xED.\r\n\r\n:::\r\n```\r\n\r\n:::div {class="mi-clase" id="seccion" style="padding: 2rem; background: #f0f0f0"}\r\n\r\nContenido **markdown** aqu\xED.\r\n\r\n:::\r\n\r\n## `:::style` \u2014 Inyectar CSS\r\n\r\nInyecta un bloque `<style>` global. \xDAtil para estilos que afectan m\xFAltiples componentes.\r\n\r\n```md\r\n:::style\r\n.nr-mi-clase { color: red; }\r\n:::\r\n```\r\n\r\n## `:::custom` \u2014 Elemento personalizado\r\n\r\nSimilar a `div`, pero permite crear cualquier elemento HTML.\r\n\r\n## `:::raw` \u2014 HTML crudo\r\n\r\nRenderiza contenido HTML sin procesar.\r\n\r\n## Props\r\n\r\n| Prop | Tipo | Descripci\xF3n |\r\n| --- | --- | --- |\r\n| `class` | texto | Clases CSS (soporta `.shorthand` tambi\xE9n) |\r\n| `id` | texto | ID del elemento |\r\n| `style` | CSS inline | Estilos inline (se aplica con `setProperty`, no sobreescribe otros estilos) |\r\n| `data-*` | texto | Cualquier atributo `data-*` se aplica al elemento |\r\n\r\n## Shorthands\r\n\r\n```md\r\n:::div {.mi-clase #mi-id}\r\n\r\nIgual que usar `class="mi-clase" id="mi-id"`.\r\n\r\n:::\r\n```'
|
|
2799
2819
|
}
|
|
2800
2820
|
];
|
|
2801
2821
|
|