@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.js
CHANGED
|
@@ -531,6 +531,22 @@ function parseHtmlAttrs(attrsString) {
|
|
|
531
531
|
}
|
|
532
532
|
|
|
533
533
|
// core/parser.ts
|
|
534
|
+
var VOID_ELEMENTS = /* @__PURE__ */ new Set([
|
|
535
|
+
"area",
|
|
536
|
+
"base",
|
|
537
|
+
"br",
|
|
538
|
+
"col",
|
|
539
|
+
"embed",
|
|
540
|
+
"hr",
|
|
541
|
+
"img",
|
|
542
|
+
"input",
|
|
543
|
+
"link",
|
|
544
|
+
"meta",
|
|
545
|
+
"param",
|
|
546
|
+
"source",
|
|
547
|
+
"track",
|
|
548
|
+
"wbr"
|
|
549
|
+
]);
|
|
534
550
|
function parseMarkdown(markdown2) {
|
|
535
551
|
if (!markdown2) return [];
|
|
536
552
|
const lines = markdown2.replace(/\r\n/g, "\n").replace(/\r/g, "").split("\n");
|
|
@@ -544,8 +560,19 @@ function parseMarkdown(markdown2) {
|
|
|
544
560
|
if (match = trimmed.match(/^(#{1,6})\s+(.+)$/)) {
|
|
545
561
|
const level = match[1].length;
|
|
546
562
|
const rawText = match[2];
|
|
547
|
-
const { text:
|
|
548
|
-
|
|
563
|
+
const { text: rawParsedText, classes: classes2, id: customId } = extractAttributes(rawText);
|
|
564
|
+
let text2 = rawParsedText;
|
|
565
|
+
let align;
|
|
566
|
+
const alignCenter = text2.match(/^->\s*(.+?)\s*<-\s*$/);
|
|
567
|
+
const alignRight = text2.match(/^->\s*(.+?)\s*->\s*$/);
|
|
568
|
+
if (alignCenter) {
|
|
569
|
+
text2 = alignCenter[1];
|
|
570
|
+
align = "center";
|
|
571
|
+
} else if (alignRight) {
|
|
572
|
+
text2 = alignRight[1];
|
|
573
|
+
align = "right";
|
|
574
|
+
}
|
|
575
|
+
const baseId = customId || generateId(text2);
|
|
549
576
|
let id2 = baseId;
|
|
550
577
|
let n = 1;
|
|
551
578
|
while (usedIds.has(id2)) {
|
|
@@ -553,7 +580,7 @@ function parseMarkdown(markdown2) {
|
|
|
553
580
|
id2 = `${baseId}-${n}`;
|
|
554
581
|
}
|
|
555
582
|
usedIds.add(id2);
|
|
556
|
-
result.push({ type: "header", level, text: text2, id: id2, classes: classes2 || void 0 });
|
|
583
|
+
result.push({ type: "header", level, text: text2, id: id2, classes: classes2 || void 0, align });
|
|
557
584
|
i++;
|
|
558
585
|
continue;
|
|
559
586
|
}
|
|
@@ -729,29 +756,13 @@ function parseMarkdown(markdown2) {
|
|
|
729
756
|
let tagStartMatch = trimmed.match(/^<([a-zA-Z][\w-]*)/);
|
|
730
757
|
if (tagStartMatch) {
|
|
731
758
|
const tagName = tagStartMatch[1].toLowerCase();
|
|
732
|
-
const voidElements = /* @__PURE__ */ new Set([
|
|
733
|
-
"area",
|
|
734
|
-
"base",
|
|
735
|
-
"br",
|
|
736
|
-
"col",
|
|
737
|
-
"embed",
|
|
738
|
-
"hr",
|
|
739
|
-
"img",
|
|
740
|
-
"input",
|
|
741
|
-
"link",
|
|
742
|
-
"meta",
|
|
743
|
-
"param",
|
|
744
|
-
"source",
|
|
745
|
-
"track",
|
|
746
|
-
"wbr"
|
|
747
|
-
]);
|
|
748
759
|
const remainingText = lines.slice(i).join("\n");
|
|
749
760
|
const openTagRegex = new RegExp(`^\\s*<${tagName}\\b([^>]*?)>`, "i");
|
|
750
761
|
const openTagMatch = remainingText.match(openTagRegex);
|
|
751
762
|
if (openTagMatch) {
|
|
752
763
|
const fullOpenTag = openTagMatch[0];
|
|
753
764
|
const attrs = openTagMatch[1].replace(/\s+/g, " ").trim();
|
|
754
|
-
const isSelfClosing = fullOpenTag.endsWith("/>") ||
|
|
765
|
+
const isSelfClosing = fullOpenTag.endsWith("/>") || VOID_ELEMENTS.has(tagName);
|
|
755
766
|
if (isSelfClosing) {
|
|
756
767
|
const blockText = remainingText.substring(0, openTagMatch.index + fullOpenTag.length);
|
|
757
768
|
const consumedLines = blockText.split("\n").length;
|
|
@@ -1054,6 +1065,7 @@ function createModal(title) {
|
|
|
1054
1065
|
header.appendChild(titleEl);
|
|
1055
1066
|
const closeBtn = document.createElement("button");
|
|
1056
1067
|
closeBtn.className = "nr-modal__close";
|
|
1068
|
+
closeBtn.setAttribute("aria-label", "Close");
|
|
1057
1069
|
closeBtn.appendChild(createIcon("close"));
|
|
1058
1070
|
closeBtn.addEventListener("click", () => dialog.close());
|
|
1059
1071
|
header.appendChild(closeBtn);
|
|
@@ -1274,11 +1286,89 @@ function parseInlinePart(part) {
|
|
|
1274
1286
|
return document.createTextNode(part);
|
|
1275
1287
|
}
|
|
1276
1288
|
|
|
1289
|
+
// vanilla/utils.ts
|
|
1290
|
+
var THEME_TOKENS = /* @__PURE__ */ new Set([
|
|
1291
|
+
"primary",
|
|
1292
|
+
"secondary",
|
|
1293
|
+
"accent",
|
|
1294
|
+
"neutral",
|
|
1295
|
+
"info",
|
|
1296
|
+
"success",
|
|
1297
|
+
"warning",
|
|
1298
|
+
"error"
|
|
1299
|
+
]);
|
|
1300
|
+
function isThemeToken(color) {
|
|
1301
|
+
return !!color && THEME_TOKENS.has(color);
|
|
1302
|
+
}
|
|
1303
|
+
function isArbitraryColor(value) {
|
|
1304
|
+
if (THEME_TOKENS.has(value)) return false;
|
|
1305
|
+
if (/^(#|rgb|hsl|oklch|oklab|lab|lch|color\()/i.test(value)) return true;
|
|
1306
|
+
if (/^[a-zA-Z]+$/.test(value)) return true;
|
|
1307
|
+
return false;
|
|
1308
|
+
}
|
|
1309
|
+
function applyBaseProps(el, props) {
|
|
1310
|
+
if (props.class) {
|
|
1311
|
+
el.classList.add(...props.class.split(/\s+/).filter(Boolean));
|
|
1312
|
+
}
|
|
1313
|
+
if (props.style) {
|
|
1314
|
+
const styles = parseCssString(props.style);
|
|
1315
|
+
for (const [key, value] of Object.entries(styles)) {
|
|
1316
|
+
el.style.setProperty(key, String(value));
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
function applyFloatStyle(el, float, width) {
|
|
1321
|
+
if (!float) return;
|
|
1322
|
+
if (float === "left" || float === "right") {
|
|
1323
|
+
el.style.float = float;
|
|
1324
|
+
if (!width) el.style.maxWidth = "50%";
|
|
1325
|
+
el.style.marginInlineStart = float === "right" ? "1rem" : "";
|
|
1326
|
+
el.style.marginInlineEnd = float === "left" ? "1rem" : "";
|
|
1327
|
+
} else if (float === "center") {
|
|
1328
|
+
el.style.marginInline = "auto";
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
function applyColor(el, color, classSuffix) {
|
|
1332
|
+
if (!color) return "";
|
|
1333
|
+
if (isThemeToken(color)) {
|
|
1334
|
+
return ` ${classSuffix}--${color}`;
|
|
1335
|
+
}
|
|
1336
|
+
if (isArbitraryColor(color)) {
|
|
1337
|
+
el.style.background = color;
|
|
1338
|
+
el.style.color = "white";
|
|
1339
|
+
}
|
|
1340
|
+
return "";
|
|
1341
|
+
}
|
|
1342
|
+
function openModal(dialog) {
|
|
1343
|
+
if (!dialog.open) {
|
|
1344
|
+
document.body.appendChild(dialog);
|
|
1345
|
+
dialog.showModal();
|
|
1346
|
+
dialog.addEventListener("close", () => dialog.remove(), { once: true });
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
var IMG_RE = /!\[([^\]]*)\]\(([^)]+)\)/g;
|
|
1350
|
+
function applyAlignClass(el, baseClass, align) {
|
|
1351
|
+
if (align === "center") {
|
|
1352
|
+
el.classList.add(`${baseClass}--center`);
|
|
1353
|
+
} else if (align === "right") {
|
|
1354
|
+
el.classList.add(`${baseClass}--right`);
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
function resolveIcon(value, fallback) {
|
|
1358
|
+
if (!value) return fallback;
|
|
1359
|
+
if (value === "none" || value === "off") return null;
|
|
1360
|
+
return value;
|
|
1361
|
+
}
|
|
1362
|
+
function parseIntProp(value, defaultValue) {
|
|
1363
|
+
if (!value) return defaultValue;
|
|
1364
|
+
const n = parseInt(value, 10);
|
|
1365
|
+
return Number.isNaN(n) ? defaultValue : n;
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1277
1368
|
// vanilla/directives/admonition.ts
|
|
1278
1369
|
var admonitionDirective = ({ directiveType, props, renderSlot }) => {
|
|
1279
1370
|
const el = createAdmonition(directiveType, props.title, props.icon);
|
|
1280
|
-
|
|
1281
|
-
if (props.style) el.setAttribute("style", props.style);
|
|
1371
|
+
applyBaseProps(el, props);
|
|
1282
1372
|
const body = el.querySelector(".nr-admonition__body");
|
|
1283
1373
|
if (body) {
|
|
1284
1374
|
body.appendChild(renderSlot("default"));
|
|
@@ -1294,8 +1384,7 @@ var detailsDirective = ({ props, renderSlot }) => {
|
|
|
1294
1384
|
props.icon,
|
|
1295
1385
|
props.defaultOpen === "true"
|
|
1296
1386
|
);
|
|
1297
|
-
|
|
1298
|
-
if (props.style) el.setAttribute("style", props.style);
|
|
1387
|
+
applyBaseProps(el, props);
|
|
1299
1388
|
const body = el.querySelector(".nr-details__body");
|
|
1300
1389
|
if (body) {
|
|
1301
1390
|
body.appendChild(renderSlot("default"));
|
|
@@ -1308,14 +1397,17 @@ var details_default = detailsDirective;
|
|
|
1308
1397
|
var modalDirective = ({ props, renderSlot }) => {
|
|
1309
1398
|
const label = props.label || props.title || "Open";
|
|
1310
1399
|
const modalTitle = props.title || "Modal";
|
|
1311
|
-
const
|
|
1400
|
+
const align = props.align || "left";
|
|
1401
|
+
const iconName = resolveIcon(props.icon, "open_in_full");
|
|
1312
1402
|
const wrapper = document.createElement("div");
|
|
1313
1403
|
wrapper.className = "nr-modal-trigger";
|
|
1404
|
+
applyAlignClass(wrapper, "nr-modal-trigger", align);
|
|
1314
1405
|
const btn = document.createElement("button");
|
|
1315
|
-
btn.className =
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1406
|
+
btn.className = "nr-button nr-button--default";
|
|
1407
|
+
btn.setAttribute("aria-haspopup", "dialog");
|
|
1408
|
+
applyColor(btn, props.color, "nr-button");
|
|
1409
|
+
applyBaseProps(btn, props);
|
|
1410
|
+
if (iconName) btn.appendChild(createIcon(iconName));
|
|
1319
1411
|
btn.appendChild(document.createTextNode(label));
|
|
1320
1412
|
const dialog = createModal(modalTitle);
|
|
1321
1413
|
const body = dialog.querySelector(".nr-modal__body");
|
|
@@ -1325,15 +1417,7 @@ var modalDirective = ({ props, renderSlot }) => {
|
|
|
1325
1417
|
prose.appendChild(renderSlot("default"));
|
|
1326
1418
|
body.appendChild(prose);
|
|
1327
1419
|
}
|
|
1328
|
-
btn.addEventListener("click", () =>
|
|
1329
|
-
if (!dialog.open) {
|
|
1330
|
-
document.body.appendChild(dialog);
|
|
1331
|
-
dialog.showModal();
|
|
1332
|
-
dialog.addEventListener("close", () => {
|
|
1333
|
-
dialog.remove();
|
|
1334
|
-
}, { once: true });
|
|
1335
|
-
}
|
|
1336
|
-
});
|
|
1420
|
+
btn.addEventListener("click", () => openModal(dialog));
|
|
1337
1421
|
wrapper.appendChild(btn);
|
|
1338
1422
|
wrapper.appendChild(dialog);
|
|
1339
1423
|
return wrapper;
|
|
@@ -1343,20 +1427,22 @@ var modal_default = modalDirective;
|
|
|
1343
1427
|
// vanilla/directives/button.ts
|
|
1344
1428
|
var buttonDirective = ({ props, renderSlot }) => {
|
|
1345
1429
|
const url = props.url || props.href || "#";
|
|
1346
|
-
const label = props.label;
|
|
1347
|
-
const
|
|
1430
|
+
const label = props.label || props.title;
|
|
1431
|
+
const iconName = resolveIcon(props.icon, "touch_app");
|
|
1348
1432
|
const target = props.target || "_blank";
|
|
1349
|
-
const
|
|
1433
|
+
const align = props.align || "left";
|
|
1350
1434
|
const wrapper = document.createElement("div");
|
|
1351
1435
|
wrapper.className = "nr-button-wrap";
|
|
1436
|
+
applyAlignClass(wrapper, "nr-button-wrap", align);
|
|
1352
1437
|
if (label) {
|
|
1353
1438
|
const a = document.createElement("a");
|
|
1354
1439
|
a.href = url;
|
|
1355
1440
|
a.target = target;
|
|
1356
|
-
a.rel = "noopener noreferrer";
|
|
1441
|
+
if (target === "_blank") a.rel = "noopener noreferrer";
|
|
1357
1442
|
a.className = "nr-button nr-button--default";
|
|
1358
|
-
|
|
1359
|
-
a
|
|
1443
|
+
applyColor(a, props.color, "nr-button");
|
|
1444
|
+
applyBaseProps(a, props);
|
|
1445
|
+
if (iconName) a.appendChild(createIcon(iconName));
|
|
1360
1446
|
a.appendChild(document.createTextNode(label));
|
|
1361
1447
|
wrapper.appendChild(a);
|
|
1362
1448
|
return wrapper;
|
|
@@ -1366,17 +1452,20 @@ var buttonDirective = ({ props, renderSlot }) => {
|
|
|
1366
1452
|
if (links.length > 0) {
|
|
1367
1453
|
links.forEach((link) => {
|
|
1368
1454
|
link.classList.add("nr-button", "nr-button--default");
|
|
1369
|
-
|
|
1455
|
+
applyColor(link, props.color, "nr-button");
|
|
1456
|
+
if (iconName) link.prepend(createIcon(iconName));
|
|
1457
|
+
applyBaseProps(link, props);
|
|
1370
1458
|
});
|
|
1371
1459
|
wrapper.appendChild(slotContent);
|
|
1372
1460
|
} else {
|
|
1373
1461
|
const a = document.createElement("a");
|
|
1374
1462
|
a.href = url;
|
|
1375
1463
|
a.target = target;
|
|
1376
|
-
a.rel = "noopener noreferrer";
|
|
1464
|
+
if (target === "_blank") a.rel = "noopener noreferrer";
|
|
1377
1465
|
a.className = "nr-button nr-button--default";
|
|
1378
|
-
|
|
1379
|
-
a
|
|
1466
|
+
applyColor(a, props.color, "nr-button");
|
|
1467
|
+
applyBaseProps(a, props);
|
|
1468
|
+
if (iconName) a.appendChild(createIcon(iconName));
|
|
1380
1469
|
a.appendChild(slotContent);
|
|
1381
1470
|
wrapper.appendChild(a);
|
|
1382
1471
|
}
|
|
@@ -1398,12 +1487,9 @@ var cardDirective = ({
|
|
|
1398
1487
|
const { isSingleCard } = options || {};
|
|
1399
1488
|
const isModal = directiveType === "card-m";
|
|
1400
1489
|
const isLink = directiveType === "card-b";
|
|
1401
|
-
const inlineStyles = props.style ? parseCssString(props.style) : {};
|
|
1402
1490
|
const card = document.createElement("div");
|
|
1403
1491
|
card.className = `nr-card${isModal || isLink ? " nr-card--interactive" : ""} ${customClass}`.trim();
|
|
1404
|
-
|
|
1405
|
-
card.style.setProperty(key, String(value));
|
|
1406
|
-
}
|
|
1492
|
+
applyBaseProps(card, props);
|
|
1407
1493
|
if (image) {
|
|
1408
1494
|
const imgWrap = document.createElement("div");
|
|
1409
1495
|
imgWrap.className = `nr-card__image${isSingleCard ? " nr-card__image--tall" : ""}`;
|
|
@@ -1482,13 +1568,7 @@ var cardDirective = ({
|
|
|
1482
1568
|
prose.appendChild(renderSlot("content") || renderSlot("default"));
|
|
1483
1569
|
modalBody.appendChild(prose);
|
|
1484
1570
|
}
|
|
1485
|
-
card.addEventListener("click", () =>
|
|
1486
|
-
if (!dialog.open) {
|
|
1487
|
-
document.body.appendChild(dialog);
|
|
1488
|
-
dialog.showModal();
|
|
1489
|
-
dialog.addEventListener("close", () => dialog.remove(), { once: true });
|
|
1490
|
-
}
|
|
1491
|
-
});
|
|
1571
|
+
card.addEventListener("click", () => openModal(dialog));
|
|
1492
1572
|
const frag = document.createDocumentFragment();
|
|
1493
1573
|
frag.appendChild(card);
|
|
1494
1574
|
frag.appendChild(dialog);
|
|
@@ -1536,8 +1616,8 @@ var slideDirective = ({
|
|
|
1536
1616
|
if (lines.length === 0) {
|
|
1537
1617
|
return document.createDocumentFragment();
|
|
1538
1618
|
}
|
|
1539
|
-
const interval =
|
|
1540
|
-
const speed =
|
|
1619
|
+
const interval = parseIntProp(props.interval, 3e3);
|
|
1620
|
+
const speed = parseIntProp(props.speed, 500);
|
|
1541
1621
|
const rawClass = props.class || "";
|
|
1542
1622
|
const inlineStyle = props.style ? parseCssString(props.style) : {};
|
|
1543
1623
|
const scopeClass = `sld-${++slideCounter}`;
|
|
@@ -1588,10 +1668,11 @@ var slideDirective = ({
|
|
|
1588
1668
|
}
|
|
1589
1669
|
});
|
|
1590
1670
|
if (lines.length > 1) {
|
|
1591
|
-
setInterval(() => {
|
|
1671
|
+
const id = setInterval(() => {
|
|
1592
1672
|
current = (current + 1) % lines.length;
|
|
1593
1673
|
track.style.transform = `translateY(${-current * maxH}px)`;
|
|
1594
1674
|
}, interval);
|
|
1675
|
+
container.dataset.nrIntervalId = String(id);
|
|
1595
1676
|
}
|
|
1596
1677
|
return container;
|
|
1597
1678
|
};
|
|
@@ -1601,8 +1682,7 @@ var slide_default = slideDirective;
|
|
|
1601
1682
|
var keysDirective = ({ props, slots }) => {
|
|
1602
1683
|
const wrap = document.createElement("div");
|
|
1603
1684
|
wrap.className = "nr-keys";
|
|
1604
|
-
|
|
1605
|
-
if (props.style) wrap.setAttribute("style", props.style);
|
|
1685
|
+
applyBaseProps(wrap, props);
|
|
1606
1686
|
const sizeClass = props.size ? ` nr-kbd--${props.size}` : "";
|
|
1607
1687
|
const parts = (slots.default || "").split("+").map((p) => p.trim()).filter(Boolean);
|
|
1608
1688
|
parts.forEach((part, i) => {
|
|
@@ -1626,8 +1706,7 @@ var accordionCounter = 0;
|
|
|
1626
1706
|
var accordionItemDirective = ({ props, renderSlot }) => {
|
|
1627
1707
|
const item = document.createElement("div");
|
|
1628
1708
|
item.className = "nr-accordion__item";
|
|
1629
|
-
|
|
1630
|
-
if (props.style) item.setAttribute("style", props.style);
|
|
1709
|
+
applyBaseProps(item, props);
|
|
1631
1710
|
const input = document.createElement("input");
|
|
1632
1711
|
input.type = "radio";
|
|
1633
1712
|
input.className = "nr-accordion__input";
|
|
@@ -1646,8 +1725,7 @@ var accordionItemDirective = ({ props, renderSlot }) => {
|
|
|
1646
1725
|
var accordionDirective = ({ props, renderSlot }) => {
|
|
1647
1726
|
const wrap = document.createElement("div");
|
|
1648
1727
|
wrap.className = "nr-accordion";
|
|
1649
|
-
|
|
1650
|
-
if (props.style) wrap.setAttribute("style", props.style);
|
|
1728
|
+
applyBaseProps(wrap, props);
|
|
1651
1729
|
wrap.appendChild(renderSlot("default"));
|
|
1652
1730
|
const mode = props.mode === "checkbox" ? "checkbox" : "radio";
|
|
1653
1731
|
const group = `nr-acc-${++accordionCounter}`;
|
|
@@ -1660,7 +1738,6 @@ var accordionDirective = ({ props, renderSlot }) => {
|
|
|
1660
1738
|
var accordion_default = accordionDirective;
|
|
1661
1739
|
|
|
1662
1740
|
// vanilla/directives/carousel.ts
|
|
1663
|
-
var IMG_RE = /!\[([^\]]*)\]\(([^)]+)\)/g;
|
|
1664
1741
|
var carouselDirective = ({ props, slots }) => {
|
|
1665
1742
|
const images = [];
|
|
1666
1743
|
const raw = slots.default || "";
|
|
@@ -1676,19 +1753,9 @@ var carouselDirective = ({ props, slots }) => {
|
|
|
1676
1753
|
wrap.className = "nr-carousel";
|
|
1677
1754
|
wrap.tabIndex = 0;
|
|
1678
1755
|
wrap.setAttribute("aria-label", "Image carousel");
|
|
1679
|
-
|
|
1680
|
-
if (props.style) wrap.setAttribute("style", props.style);
|
|
1756
|
+
applyBaseProps(wrap, props);
|
|
1681
1757
|
if (props.width) wrap.style.width = props.width;
|
|
1682
|
-
|
|
1683
|
-
if (props.float === "left" || props.float === "right") {
|
|
1684
|
-
wrap.style.float = props.float;
|
|
1685
|
-
if (!props.width) wrap.style.maxWidth = "50%";
|
|
1686
|
-
wrap.style.marginInlineStart = props.float === "right" ? "1rem" : "";
|
|
1687
|
-
wrap.style.marginInlineEnd = props.float === "left" ? "1rem" : "";
|
|
1688
|
-
} else if (props.float === "center") {
|
|
1689
|
-
wrap.style.marginInline = "auto";
|
|
1690
|
-
}
|
|
1691
|
-
}
|
|
1758
|
+
applyFloatStyle(wrap, props.float, props.width);
|
|
1692
1759
|
const viewport = document.createElement("div");
|
|
1693
1760
|
viewport.className = "nr-carousel__viewport";
|
|
1694
1761
|
if (props.height) viewport.style.height = props.height;
|
|
@@ -1758,11 +1825,10 @@ var DEFAULT_LABELS = ["days", "hours", "min", "sec"];
|
|
|
1758
1825
|
var countdownDirective = ({ props }) => {
|
|
1759
1826
|
const wrap = document.createElement("div");
|
|
1760
1827
|
wrap.className = "nr-countdown";
|
|
1761
|
-
|
|
1762
|
-
if (props.style) wrap.setAttribute("style", props.style);
|
|
1828
|
+
applyBaseProps(wrap, props);
|
|
1763
1829
|
const labelParts = (props.labels || "").split("|").map((s) => s.trim());
|
|
1764
1830
|
const labels = DEFAULT_LABELS.map((label, i) => labelParts[i] || label);
|
|
1765
|
-
const digits =
|
|
1831
|
+
const digits = parseIntProp(props.digits, 2);
|
|
1766
1832
|
const targetTime = props.target ? new Date(props.target).getTime() : NaN;
|
|
1767
1833
|
const hasTarget = !Number.isNaN(targetTime);
|
|
1768
1834
|
const blocks = [];
|
|
@@ -1809,13 +1875,15 @@ var countdownDirective = ({ props }) => {
|
|
|
1809
1875
|
blocks.push({ value });
|
|
1810
1876
|
});
|
|
1811
1877
|
render();
|
|
1812
|
-
if (hasTarget)
|
|
1878
|
+
if (hasTarget) {
|
|
1879
|
+
const id = setInterval(render, 1e3);
|
|
1880
|
+
wrap.dataset.nrIntervalId = String(id);
|
|
1881
|
+
}
|
|
1813
1882
|
return wrap;
|
|
1814
1883
|
};
|
|
1815
1884
|
var countdown_default = countdownDirective;
|
|
1816
1885
|
|
|
1817
1886
|
// vanilla/directives/diff.ts
|
|
1818
|
-
var IMG_RE2 = /!\[([^\]]*)\]\(([^)]+)\)/g;
|
|
1819
1887
|
var diffDirective = ({ props, slots }) => {
|
|
1820
1888
|
let before = (props.before || "").split("#")[0].trim();
|
|
1821
1889
|
let after = (props.after || "").split("#")[0].trim();
|
|
@@ -1823,8 +1891,8 @@ var diffDirective = ({ props, slots }) => {
|
|
|
1823
1891
|
const urls = [];
|
|
1824
1892
|
const raw = slots.default || "";
|
|
1825
1893
|
let m;
|
|
1826
|
-
|
|
1827
|
-
while ((m =
|
|
1894
|
+
IMG_RE.lastIndex = 0;
|
|
1895
|
+
while ((m = IMG_RE.exec(raw)) !== null) {
|
|
1828
1896
|
urls.push(m[2].split("#")[0].trim());
|
|
1829
1897
|
}
|
|
1830
1898
|
if (!before && urls.length > 0) before = urls[0];
|
|
@@ -1837,21 +1905,11 @@ var diffDirective = ({ props, slots }) => {
|
|
|
1837
1905
|
figure.className = "nr-diff";
|
|
1838
1906
|
figure.tabIndex = 0;
|
|
1839
1907
|
figure.setAttribute("aria-label", "Image comparison slider");
|
|
1840
|
-
|
|
1841
|
-
if (props.style) figure.setAttribute("style", props.style);
|
|
1908
|
+
applyBaseProps(figure, props);
|
|
1842
1909
|
if (props.aspect) figure.style.aspectRatio = props.aspect;
|
|
1843
1910
|
if (props.height) figure.style.height = props.height;
|
|
1844
1911
|
if (props.width) figure.style.width = props.width;
|
|
1845
|
-
|
|
1846
|
-
if (props.float === "left" || props.float === "right") {
|
|
1847
|
-
figure.style.float = props.float;
|
|
1848
|
-
if (!props.width) figure.style.maxWidth = "50%";
|
|
1849
|
-
figure.style.marginInlineStart = props.float === "right" ? "1rem" : "";
|
|
1850
|
-
figure.style.marginInlineEnd = props.float === "left" ? "1rem" : "";
|
|
1851
|
-
} else if (props.float === "center") {
|
|
1852
|
-
figure.style.marginInline = "auto";
|
|
1853
|
-
}
|
|
1854
|
-
}
|
|
1912
|
+
applyFloatStyle(figure, props.float, props.width);
|
|
1855
1913
|
const beforeItem = document.createElement("div");
|
|
1856
1914
|
beforeItem.className = "nr-diff__item nr-diff__item--before";
|
|
1857
1915
|
beforeItem.setAttribute("role", "img");
|
|
@@ -1914,8 +1972,7 @@ var diff_default = diffDirective;
|
|
|
1914
1972
|
var hover3dDirective = ({ props, renderSlot }) => {
|
|
1915
1973
|
const container = document.createElement("div");
|
|
1916
1974
|
container.className = "nr-hover-3d";
|
|
1917
|
-
|
|
1918
|
-
if (props.style) container.setAttribute("style", props.style);
|
|
1975
|
+
applyBaseProps(container, props);
|
|
1919
1976
|
const stage = document.createElement("div");
|
|
1920
1977
|
stage.className = "nr-hover-3d__stage";
|
|
1921
1978
|
stage.appendChild(renderSlot("default"));
|
|
@@ -1928,14 +1985,13 @@ var hover3dDirective = ({ props, renderSlot }) => {
|
|
|
1928
1985
|
var hover3d_default = hover3dDirective;
|
|
1929
1986
|
|
|
1930
1987
|
// vanilla/directives/hovergallery.ts
|
|
1931
|
-
var IMG_RE3 = /!\[([^\]]*)\]\(([^)]+)\)/g;
|
|
1932
1988
|
var MAX_IMAGES = 10;
|
|
1933
1989
|
var hovergalleryDirective = ({ props, slots }) => {
|
|
1934
1990
|
const images = [];
|
|
1935
1991
|
const raw = slots.default || "";
|
|
1936
1992
|
let m;
|
|
1937
|
-
|
|
1938
|
-
while ((m =
|
|
1993
|
+
IMG_RE.lastIndex = 0;
|
|
1994
|
+
while ((m = IMG_RE.exec(raw)) !== null) {
|
|
1939
1995
|
images.push({ src: m[2].split("#")[0].trim(), alt: m[1].trim() || "gallery image" });
|
|
1940
1996
|
}
|
|
1941
1997
|
if (images.length === 0) {
|
|
@@ -1944,9 +2000,8 @@ var hovergalleryDirective = ({ props, slots }) => {
|
|
|
1944
2000
|
const count = Math.min(images.length, MAX_IMAGES);
|
|
1945
2001
|
const figure = document.createElement("figure");
|
|
1946
2002
|
figure.className = "nr-hover-gallery";
|
|
2003
|
+
applyBaseProps(figure, props);
|
|
1947
2004
|
if (props.aspect) figure.style.aspectRatio = props.aspect;
|
|
1948
|
-
if (props.class) figure.classList.add(...props.class.split(/\s+/).filter(Boolean));
|
|
1949
|
-
if (props.style) figure.setAttribute("style", (figure.getAttribute("style") || "") + ";" + props.style);
|
|
1950
2005
|
const imgEls = [];
|
|
1951
2006
|
for (let i = 0; i < count; i++) {
|
|
1952
2007
|
const el = document.createElement("img");
|
|
@@ -1996,28 +2051,11 @@ var hovergalleryDirective = ({ props, slots }) => {
|
|
|
1996
2051
|
var hovergallery_default = hovergalleryDirective;
|
|
1997
2052
|
|
|
1998
2053
|
// vanilla/directives/chat.ts
|
|
1999
|
-
var CHAT_THEME_TOKENS = /* @__PURE__ */ new Set([
|
|
2000
|
-
"primary",
|
|
2001
|
-
"secondary",
|
|
2002
|
-
"accent",
|
|
2003
|
-
"neutral",
|
|
2004
|
-
"info",
|
|
2005
|
-
"success",
|
|
2006
|
-
"warning",
|
|
2007
|
-
"error"
|
|
2008
|
-
]);
|
|
2009
|
-
function isArbitraryColor(value) {
|
|
2010
|
-
if (CHAT_THEME_TOKENS.has(value)) return false;
|
|
2011
|
-
if (/^(#|rgb|hsl|oklch|oklab|lab|lch|color\()/i.test(value)) return true;
|
|
2012
|
-
if (/^[a-zA-Z]+$/.test(value)) return true;
|
|
2013
|
-
return false;
|
|
2014
|
-
}
|
|
2015
2054
|
var chatItemDirective = ({ props, renderSlot }) => {
|
|
2016
2055
|
const side = props.side === "end" ? "end" : "start";
|
|
2017
2056
|
const wrap = document.createElement("div");
|
|
2018
2057
|
wrap.className = `nr-chat nr-chat--${side}`;
|
|
2019
|
-
|
|
2020
|
-
if (props.style) wrap.setAttribute("style", props.style);
|
|
2058
|
+
applyBaseProps(wrap, props);
|
|
2021
2059
|
const header = document.createElement("div");
|
|
2022
2060
|
header.className = "nr-chat__header";
|
|
2023
2061
|
if (props.name) {
|
|
@@ -2042,14 +2080,8 @@ var chatItemDirective = ({ props, renderSlot }) => {
|
|
|
2042
2080
|
avatar.appendChild(img);
|
|
2043
2081
|
wrap.appendChild(avatar);
|
|
2044
2082
|
}
|
|
2045
|
-
const isThemeToken = CHAT_THEME_TOKENS.has(props.color || "");
|
|
2046
|
-
const colorClass = isThemeToken ? ` nr-chat__bubble--${props.color}` : "";
|
|
2047
2083
|
const bubble = document.createElement("div");
|
|
2048
|
-
bubble.className = `nr-chat__bubble${
|
|
2049
|
-
if (props.color && isArbitraryColor(props.color) && !isThemeToken) {
|
|
2050
|
-
bubble.style.background = props.color;
|
|
2051
|
-
bubble.style.color = "white";
|
|
2052
|
-
}
|
|
2084
|
+
bubble.className = `nr-chat__bubble${applyColor(bubble, props.color, "nr-chat__bubble")}`;
|
|
2053
2085
|
bubble.appendChild(renderSlot("default"));
|
|
2054
2086
|
wrap.appendChild(bubble);
|
|
2055
2087
|
if (props.footer) {
|
|
@@ -2063,8 +2095,7 @@ var chatItemDirective = ({ props, renderSlot }) => {
|
|
|
2063
2095
|
var chatDirective = ({ props, renderSlot }) => {
|
|
2064
2096
|
const wrap = document.createElement("div");
|
|
2065
2097
|
wrap.className = "nr-chat";
|
|
2066
|
-
|
|
2067
|
-
if (props.style) wrap.setAttribute("style", props.style);
|
|
2098
|
+
applyBaseProps(wrap, props);
|
|
2068
2099
|
wrap.appendChild(renderSlot("default"));
|
|
2069
2100
|
return wrap;
|
|
2070
2101
|
};
|
|
@@ -2100,8 +2131,7 @@ function bindEventProp(el, eventProp) {
|
|
|
2100
2131
|
var richlistItemDirective = ({ props, renderSlot }) => {
|
|
2101
2132
|
const li = document.createElement("li");
|
|
2102
2133
|
li.className = "nr-richlist__item";
|
|
2103
|
-
|
|
2104
|
-
if (props.style) li.setAttribute("style", props.style);
|
|
2134
|
+
applyBaseProps(li, props);
|
|
2105
2135
|
if (props.image) {
|
|
2106
2136
|
const thumb = document.createElement("div");
|
|
2107
2137
|
thumb.className = "nr-richlist__thumb";
|
|
@@ -2163,36 +2193,20 @@ var richlistItemDirective = ({ props, renderSlot }) => {
|
|
|
2163
2193
|
var richlistDirective = ({ props, renderSlot }) => {
|
|
2164
2194
|
const ul = document.createElement("ul");
|
|
2165
2195
|
ul.className = "nr-richlist";
|
|
2166
|
-
|
|
2167
|
-
if (props.style) ul.setAttribute("style", props.style);
|
|
2196
|
+
applyBaseProps(ul, props);
|
|
2168
2197
|
ul.appendChild(renderSlot("default"));
|
|
2169
2198
|
return ul;
|
|
2170
2199
|
};
|
|
2171
2200
|
var richlist_default = richlistDirective;
|
|
2172
2201
|
|
|
2173
2202
|
// vanilla/directives/stat.ts
|
|
2174
|
-
var STAT_THEME_TOKENS = /* @__PURE__ */ new Set([
|
|
2175
|
-
"primary",
|
|
2176
|
-
"secondary",
|
|
2177
|
-
"info",
|
|
2178
|
-
"success",
|
|
2179
|
-
"warning",
|
|
2180
|
-
"error"
|
|
2181
|
-
]);
|
|
2182
|
-
function isArbitraryColor2(value) {
|
|
2183
|
-
if (STAT_THEME_TOKENS.has(value)) return false;
|
|
2184
|
-
if (/^(#|rgb|hsl|oklch|oklab|lab|lch|color\()/i.test(value)) return true;
|
|
2185
|
-
if (/^[a-zA-Z]+$/.test(value)) return true;
|
|
2186
|
-
return false;
|
|
2187
|
-
}
|
|
2188
2203
|
var statDirective = ({ props }) => {
|
|
2189
|
-
const
|
|
2190
|
-
const colorClass =
|
|
2204
|
+
const statIsThemeToken = isThemeToken(props.color);
|
|
2205
|
+
const colorClass = statIsThemeToken ? ` nr-stat--${props.color}` : "";
|
|
2191
2206
|
const stat = document.createElement("div");
|
|
2192
2207
|
stat.className = `nr-stat${colorClass}`;
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
const useInlineColor = props.color && isArbitraryColor2(props.color) && !isThemeToken;
|
|
2208
|
+
applyBaseProps(stat, props);
|
|
2209
|
+
const useInlineColor = props.color && isArbitraryColor(props.color) && !statIsThemeToken;
|
|
2196
2210
|
if (props.icon) {
|
|
2197
2211
|
const figure = document.createElement("div");
|
|
2198
2212
|
figure.className = "nr-stat__figure";
|
|
@@ -2285,9 +2299,12 @@ function renderHtmlString(html) {
|
|
|
2285
2299
|
processedContent = processedContent.replace(
|
|
2286
2300
|
/<style(?:\s+[^>]*)?>([\s\S]*?)<\/style>/gi,
|
|
2287
2301
|
(_match, cssContent) => {
|
|
2302
|
+
const trimmed = cssContent.trim();
|
|
2303
|
+
const existing = document.head.querySelector("style[data-nr-global]");
|
|
2304
|
+
if (existing && existing.textContent === trimmed) return "";
|
|
2288
2305
|
const styleEl = document.createElement("style");
|
|
2289
2306
|
styleEl.setAttribute("data-nr-global", "");
|
|
2290
|
-
styleEl.textContent =
|
|
2307
|
+
styleEl.textContent = trimmed;
|
|
2291
2308
|
document.head.appendChild(styleEl);
|
|
2292
2309
|
return "";
|
|
2293
2310
|
}
|
|
@@ -2357,19 +2374,14 @@ function renderElement(element, ctx, allElements) {
|
|
|
2357
2374
|
switch (element.type) {
|
|
2358
2375
|
case "header": {
|
|
2359
2376
|
const tag = `h${element.level}`;
|
|
2360
|
-
let text = element.text;
|
|
2361
|
-
const alignCenter = text.match(/^->\s*(.+?)\s*<-\s*$/);
|
|
2362
|
-
const alignRight = text.match(/^->\s*(.+?)\s*->\s*$/);
|
|
2363
|
-
if (alignCenter) text = alignCenter[1];
|
|
2364
|
-
else if (alignRight) text = alignRight[1];
|
|
2365
2377
|
const h = document.createElement(tag);
|
|
2366
2378
|
h.id = element.id;
|
|
2367
2379
|
let cls = `md-h${element.level}`;
|
|
2368
|
-
if (
|
|
2369
|
-
if (
|
|
2380
|
+
if (element.align === "center") cls += " text-center";
|
|
2381
|
+
else if (element.align === "right") cls += " text-right";
|
|
2370
2382
|
if (element.classes) cls += ` ${element.classes}`;
|
|
2371
2383
|
h.className = cls;
|
|
2372
|
-
h.appendChild(renderInline(text));
|
|
2384
|
+
h.appendChild(renderInline(element.text));
|
|
2373
2385
|
return h;
|
|
2374
2386
|
}
|
|
2375
2387
|
case "paragraph": {
|
|
@@ -2521,7 +2533,7 @@ var guideData = [
|
|
|
2521
2533
|
"title": "Introducci\xF3n",
|
|
2522
2534
|
"icon": "menu_book",
|
|
2523
2535
|
"order": 1,
|
|
2524
|
-
"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
|
|
2536
|
+
"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.'
|
|
2525
2537
|
},
|
|
2526
2538
|
{
|
|
2527
2539
|
"id": "titulos",
|
|
@@ -2737,7 +2749,7 @@ var guideData = [
|
|
|
2737
2749
|
"title": "Modal",
|
|
2738
2750
|
"icon": "open_in_full",
|
|
2739
2751
|
"order": 2,
|
|
2740
|
-
"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 (
|
|
2752
|
+
"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.'
|
|
2741
2753
|
},
|
|
2742
2754
|
{
|
|
2743
2755
|
"id": "button",
|
|
@@ -2745,7 +2757,7 @@ var guideData = [
|
|
|
2745
2757
|
"title": "Button",
|
|
2746
2758
|
"icon": "touch_app",
|
|
2747
2759
|
"order": 3,
|
|
2748
|
-
"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 `
|
|
2760
|
+
"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"`) |'
|
|
2749
2761
|
},
|
|
2750
2762
|
{
|
|
2751
2763
|
"id": "slide",
|
|
@@ -2762,6 +2774,14 @@ var guideData = [
|
|
|
2762
2774
|
"icon": "code_off",
|
|
2763
2775
|
"order": 1,
|
|
2764
2776
|
"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.'
|
|
2777
|
+
},
|
|
2778
|
+
{
|
|
2779
|
+
"id": "wrapper-directives",
|
|
2780
|
+
"category": "Layout",
|
|
2781
|
+
"title": "Wrapper Directives",
|
|
2782
|
+
"icon": "crop_free",
|
|
2783
|
+
"order": 1,
|
|
2784
|
+
"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```'
|
|
2765
2785
|
}
|
|
2766
2786
|
];
|
|
2767
2787
|
|