@noirmd/previewer 2.0.1 → 2.0.3

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 CHANGED
@@ -33,7 +33,7 @@ __export(NReditor_exports, {
33
33
  default: () => NReditor_default
34
34
  });
35
35
  module.exports = __toCommonJS(NReditor_exports);
36
- var import_react4 = __toESM(require("react"), 1);
36
+ var import_react5 = __toESM(require("react"), 1);
37
37
  var import_react_codemirror = __toESM(require("@uiw/react-codemirror"), 1);
38
38
  var import_view = require("@codemirror/view");
39
39
 
@@ -1631,6 +1631,553 @@ var slideDirective = ({
1631
1631
  };
1632
1632
  var slide_default = slideDirective;
1633
1633
 
1634
+ // vanilla/directives/keys.ts
1635
+ var keysDirective = ({ props, slots }) => {
1636
+ const wrap = document.createElement("div");
1637
+ wrap.className = "nr-keys";
1638
+ if (props.class) wrap.classList.add(...props.class.split(/\s+/).filter(Boolean));
1639
+ if (props.style) wrap.setAttribute("style", props.style);
1640
+ const sizeClass = props.size ? ` nr-kbd--${props.size}` : "";
1641
+ const parts = (slots.default || "").split("+").map((p) => p.trim()).filter(Boolean);
1642
+ parts.forEach((part, i) => {
1643
+ if (i > 0) {
1644
+ const sep = document.createElement("span");
1645
+ sep.className = "nr-keys__sep";
1646
+ sep.textContent = "+";
1647
+ wrap.appendChild(sep);
1648
+ }
1649
+ const kbd = document.createElement("kbd");
1650
+ kbd.className = `nr-kbd${sizeClass}`;
1651
+ kbd.textContent = part;
1652
+ wrap.appendChild(kbd);
1653
+ });
1654
+ return wrap;
1655
+ };
1656
+ var keys_default = keysDirective;
1657
+
1658
+ // vanilla/directives/accordion.ts
1659
+ var accordionCounter = 0;
1660
+ var accordionItemDirective = ({ props, renderSlot }) => {
1661
+ const item = document.createElement("div");
1662
+ item.className = "nr-accordion__item";
1663
+ if (props.class) item.classList.add(...props.class.split(/\s+/).filter(Boolean));
1664
+ if (props.style) item.setAttribute("style", props.style);
1665
+ const input = document.createElement("input");
1666
+ input.type = "radio";
1667
+ input.className = "nr-accordion__input";
1668
+ if (props.checked === "true" || props.checked === "") input.checked = true;
1669
+ if (props.value) input.value = props.value;
1670
+ input.setAttribute("aria-label", props.title || "Accordion item");
1671
+ const title = document.createElement("div");
1672
+ title.className = "nr-accordion__title";
1673
+ title.textContent = props.title || "";
1674
+ const content = document.createElement("div");
1675
+ content.className = "nr-accordion__content";
1676
+ content.appendChild(renderSlot("default"));
1677
+ item.append(input, title, content);
1678
+ return item;
1679
+ };
1680
+ var accordionDirective = ({ props, renderSlot }) => {
1681
+ const wrap = document.createElement("div");
1682
+ wrap.className = "nr-accordion";
1683
+ if (props.class) wrap.classList.add(...props.class.split(/\s+/).filter(Boolean));
1684
+ if (props.style) wrap.setAttribute("style", props.style);
1685
+ wrap.appendChild(renderSlot("default"));
1686
+ const mode = props.mode === "checkbox" ? "checkbox" : "radio";
1687
+ const group = `nr-acc-${++accordionCounter}`;
1688
+ wrap.querySelectorAll(".nr-accordion__input").forEach((input) => {
1689
+ input.type = mode;
1690
+ if (mode === "radio") input.name = group;
1691
+ });
1692
+ return wrap;
1693
+ };
1694
+ var accordion_default = accordionDirective;
1695
+
1696
+ // vanilla/directives/carousel.ts
1697
+ var IMG_RE = /!\[([^\]]*)\]\(([^)]+)\)/g;
1698
+ var carouselDirective = ({ props, slots }) => {
1699
+ const images = [];
1700
+ const raw = slots.default || "";
1701
+ let m;
1702
+ IMG_RE.lastIndex = 0;
1703
+ while ((m = IMG_RE.exec(raw)) !== null) {
1704
+ images.push({ src: m[2].split("#")[0].trim(), alt: m[1].trim() || "carousel image" });
1705
+ }
1706
+ if (images.length === 0) {
1707
+ return document.createDocumentFragment();
1708
+ }
1709
+ const wrap = document.createElement("div");
1710
+ wrap.className = "nr-carousel";
1711
+ wrap.tabIndex = 0;
1712
+ wrap.setAttribute("aria-label", "Image carousel");
1713
+ if (props.class) wrap.classList.add(...props.class.split(/\s+/).filter(Boolean));
1714
+ if (props.style) wrap.setAttribute("style", props.style);
1715
+ if (props.width) wrap.style.width = props.width;
1716
+ if (props.float) {
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
+ }
1726
+ const viewport = document.createElement("div");
1727
+ viewport.className = "nr-carousel__viewport";
1728
+ if (props.height) viewport.style.height = props.height;
1729
+ if (props.aspect) viewport.style.aspectRatio = props.aspect;
1730
+ const track = document.createElement("div");
1731
+ track.className = "nr-carousel__track";
1732
+ const items = [];
1733
+ for (const img of images) {
1734
+ const item = document.createElement("div");
1735
+ item.className = "nr-carousel__item";
1736
+ const el = document.createElement("img");
1737
+ el.src = img.src;
1738
+ el.alt = img.alt;
1739
+ el.loading = "lazy";
1740
+ item.appendChild(el);
1741
+ track.appendChild(item);
1742
+ items.push(item);
1743
+ }
1744
+ viewport.appendChild(track);
1745
+ wrap.appendChild(viewport);
1746
+ const total = items.length;
1747
+ let index = 0;
1748
+ const goTo = (i) => {
1749
+ index = (i % total + total) % total;
1750
+ track.style.transform = `translateX(-${index * 100}%)`;
1751
+ items.forEach((it, j) => it.classList.toggle("nr-carousel__item--active", j === index));
1752
+ dots.forEach((d, j) => d.classList.toggle("nr-carousel__dot--active", j === index));
1753
+ };
1754
+ const prev = document.createElement("button");
1755
+ prev.className = "nr-carousel__nav nr-carousel__nav--prev";
1756
+ prev.setAttribute("aria-label", "Previous slide");
1757
+ prev.textContent = "\u276E";
1758
+ prev.addEventListener("click", () => goTo(index - 1));
1759
+ const next = document.createElement("button");
1760
+ next.className = "nr-carousel__nav nr-carousel__nav--next";
1761
+ next.setAttribute("aria-label", "Next slide");
1762
+ next.textContent = "\u276F";
1763
+ next.addEventListener("click", () => goTo(index + 1));
1764
+ const dots = [];
1765
+ const dotsBox = document.createElement("div");
1766
+ dotsBox.className = "nr-carousel__dots";
1767
+ images.forEach((_, i) => {
1768
+ const dot = document.createElement("button");
1769
+ dot.className = "nr-carousel__dot";
1770
+ dot.setAttribute("aria-label", `Go to slide ${i + 1}`);
1771
+ dot.addEventListener("click", () => goTo(i));
1772
+ dotsBox.appendChild(dot);
1773
+ dots.push(dot);
1774
+ });
1775
+ wrap.append(prev, next, dotsBox);
1776
+ wrap.addEventListener("keydown", (e) => {
1777
+ if (e.key === "ArrowLeft") {
1778
+ e.preventDefault();
1779
+ goTo(index - 1);
1780
+ } else if (e.key === "ArrowRight") {
1781
+ e.preventDefault();
1782
+ goTo(index + 1);
1783
+ }
1784
+ });
1785
+ goTo(0);
1786
+ return wrap;
1787
+ };
1788
+ var carousel_default = carouselDirective;
1789
+
1790
+ // vanilla/directives/countdown.ts
1791
+ var DEFAULT_LABELS = ["days", "hours", "min", "sec"];
1792
+ var countdownDirective = ({ props }) => {
1793
+ const wrap = document.createElement("div");
1794
+ wrap.className = "nr-countdown";
1795
+ if (props.class) wrap.classList.add(...props.class.split(/\s+/).filter(Boolean));
1796
+ if (props.style) wrap.setAttribute("style", props.style);
1797
+ const labelParts = (props.labels || "").split("|").map((s) => s.trim());
1798
+ const labels = DEFAULT_LABELS.map((label, i) => labelParts[i] || label);
1799
+ const digits = parseInt(props.digits || "2", 10);
1800
+ const targetTime = props.target ? new Date(props.target).getTime() : NaN;
1801
+ const hasTarget = !Number.isNaN(targetTime);
1802
+ const blocks = [];
1803
+ const compute = () => {
1804
+ if (hasTarget) {
1805
+ const diff2 = Math.max(0, targetTime - Date.now());
1806
+ return [
1807
+ Math.floor(diff2 / 864e5),
1808
+ Math.floor(diff2 / 36e5) % 24,
1809
+ Math.floor(diff2 / 6e4) % 60,
1810
+ Math.floor(diff2 / 1e3) % 60
1811
+ ];
1812
+ }
1813
+ return [
1814
+ parseInt(props.days || "0", 10),
1815
+ parseInt(props.hours || "0", 10),
1816
+ parseInt(props.min || "0", 10),
1817
+ parseInt(props.sec || "0", 10)
1818
+ ];
1819
+ };
1820
+ const render = () => {
1821
+ const values = compute();
1822
+ blocks.forEach((block, i) => {
1823
+ const v = String(values[i]);
1824
+ block.value.style.setProperty("--value", v);
1825
+ block.value.setAttribute("aria-label", v);
1826
+ block.value.textContent = v;
1827
+ });
1828
+ };
1829
+ labels.forEach((label) => {
1830
+ const block = document.createElement("div");
1831
+ block.className = "nr-countdown__block";
1832
+ const value = document.createElement("span");
1833
+ value.className = "nr-countdown__value";
1834
+ value.style.setProperty("--digits", String(digits));
1835
+ value.setAttribute("aria-live", "polite");
1836
+ value.setAttribute("aria-label", "0");
1837
+ value.textContent = "0";
1838
+ const labelEl = document.createElement("span");
1839
+ labelEl.className = "nr-countdown__label";
1840
+ labelEl.textContent = label;
1841
+ block.append(value, labelEl);
1842
+ wrap.appendChild(block);
1843
+ blocks.push({ value });
1844
+ });
1845
+ render();
1846
+ if (hasTarget) setInterval(render, 1e3);
1847
+ return wrap;
1848
+ };
1849
+ var countdown_default = countdownDirective;
1850
+
1851
+ // vanilla/directives/diff.ts
1852
+ var IMG_RE2 = /!\[([^\]]*)\]\(([^)]+)\)/g;
1853
+ var diffDirective = ({ props, slots }) => {
1854
+ let before = (props.before || "").split("#")[0].trim();
1855
+ let after = (props.after || "").split("#")[0].trim();
1856
+ if (!before || !after) {
1857
+ const urls = [];
1858
+ const raw = slots.default || "";
1859
+ let m;
1860
+ IMG_RE2.lastIndex = 0;
1861
+ while ((m = IMG_RE2.exec(raw)) !== null) {
1862
+ urls.push(m[2].split("#")[0].trim());
1863
+ }
1864
+ if (!before && urls.length > 0) before = urls[0];
1865
+ if (!after && urls.length > 1) after = urls[1];
1866
+ }
1867
+ if (!before || !after) {
1868
+ return document.createDocumentFragment();
1869
+ }
1870
+ const figure = document.createElement("figure");
1871
+ figure.className = "nr-diff";
1872
+ figure.tabIndex = 0;
1873
+ figure.setAttribute("aria-label", "Image comparison slider");
1874
+ if (props.class) figure.classList.add(...props.class.split(/\s+/).filter(Boolean));
1875
+ if (props.style) figure.setAttribute("style", props.style);
1876
+ if (props.aspect) figure.style.aspectRatio = props.aspect;
1877
+ if (props.height) figure.style.height = props.height;
1878
+ if (props.width) figure.style.width = props.width;
1879
+ if (props.float) {
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
+ }
1889
+ const beforeItem = document.createElement("div");
1890
+ beforeItem.className = "nr-diff__item nr-diff__item--before";
1891
+ beforeItem.setAttribute("role", "img");
1892
+ beforeItem.tabIndex = 0;
1893
+ const beforeImg = document.createElement("img");
1894
+ beforeImg.src = before;
1895
+ beforeImg.alt = "before";
1896
+ beforeItem.appendChild(beforeImg);
1897
+ const afterItem = document.createElement("div");
1898
+ afterItem.className = "nr-diff__item nr-diff__item--after";
1899
+ afterItem.setAttribute("role", "img");
1900
+ const afterImg = document.createElement("img");
1901
+ afterImg.src = after;
1902
+ afterImg.alt = "after";
1903
+ afterItem.appendChild(afterImg);
1904
+ const resizer = document.createElement("div");
1905
+ resizer.className = "nr-diff__resizer";
1906
+ resizer.setAttribute("aria-label", "Drag to compare");
1907
+ resizer.title = "Drag to compare";
1908
+ figure.append(beforeItem, afterItem, resizer);
1909
+ let pos = 50;
1910
+ const applyPos = () => {
1911
+ figure.style.setProperty("--nr-diff-pos", `${pos}%`);
1912
+ };
1913
+ const setPosFromClientX = (clientX) => {
1914
+ const rect = figure.getBoundingClientRect();
1915
+ if (rect.width === 0) return;
1916
+ pos = Math.min(100, Math.max(0, (clientX - rect.left) / rect.width * 100));
1917
+ applyPos();
1918
+ };
1919
+ resizer.addEventListener("pointerdown", (e) => {
1920
+ e.preventDefault();
1921
+ resizer.setPointerCapture(e.pointerId);
1922
+ setPosFromClientX(e.clientX);
1923
+ });
1924
+ resizer.addEventListener("pointermove", (e) => {
1925
+ if (e.buttons & 1) setPosFromClientX(e.clientX);
1926
+ });
1927
+ resizer.addEventListener("pointerup", (e) => {
1928
+ if (resizer.hasPointerCapture(e.pointerId)) {
1929
+ resizer.releasePointerCapture(e.pointerId);
1930
+ }
1931
+ });
1932
+ figure.addEventListener("click", (e) => {
1933
+ if (e.target === resizer) return;
1934
+ setPosFromClientX(e.clientX);
1935
+ });
1936
+ figure.addEventListener("keydown", (e) => {
1937
+ if (e.key !== "ArrowLeft" && e.key !== "ArrowRight") return;
1938
+ e.preventDefault();
1939
+ pos = Math.min(100, Math.max(0, pos + (e.key === "ArrowRight" ? 5 : -5)));
1940
+ applyPos();
1941
+ });
1942
+ applyPos();
1943
+ return figure;
1944
+ };
1945
+ var diff_default = diffDirective;
1946
+
1947
+ // vanilla/directives/hover3d.ts
1948
+ var hover3dDirective = ({ props, renderSlot }) => {
1949
+ const container = document.createElement("div");
1950
+ container.className = "nr-hover-3d";
1951
+ if (props.class) container.classList.add(...props.class.split(/\s+/).filter(Boolean));
1952
+ if (props.style) container.setAttribute("style", props.style);
1953
+ const stage = document.createElement("div");
1954
+ stage.className = "nr-hover-3d__stage";
1955
+ stage.appendChild(renderSlot("default"));
1956
+ container.appendChild(stage);
1957
+ for (let i = 0; i < 8; i++) {
1958
+ container.appendChild(document.createElement("div"));
1959
+ }
1960
+ return container;
1961
+ };
1962
+ var hover3d_default = hover3dDirective;
1963
+
1964
+ // vanilla/directives/hovergallery.ts
1965
+ var IMG_RE3 = /!\[([^\]]*)\]\(([^)]+)\)/g;
1966
+ var hovergalleryDirective = ({ props, slots }) => {
1967
+ const images = [];
1968
+ const raw = slots.default || "";
1969
+ let m;
1970
+ IMG_RE3.lastIndex = 0;
1971
+ while ((m = IMG_RE3.exec(raw)) !== null) {
1972
+ images.push({ src: m[2].split("#")[0].trim(), alt: m[1].trim() || "gallery image" });
1973
+ }
1974
+ if (images.length === 0) {
1975
+ return document.createDocumentFragment();
1976
+ }
1977
+ const figure = document.createElement("figure");
1978
+ figure.className = "nr-hover-gallery";
1979
+ if (props.aspect) figure.style.aspectRatio = props.aspect;
1980
+ if (props.class) figure.classList.add(...props.class.split(/\s+/).filter(Boolean));
1981
+ if (props.style) figure.setAttribute("style", props.style);
1982
+ for (const img of images) {
1983
+ const el = document.createElement("img");
1984
+ el.src = img.src;
1985
+ el.alt = img.alt;
1986
+ el.loading = "lazy";
1987
+ figure.appendChild(el);
1988
+ }
1989
+ return figure;
1990
+ };
1991
+ var hovergallery_default = hovergalleryDirective;
1992
+
1993
+ // vanilla/directives/chat.ts
1994
+ var chatItemDirective = ({ props, renderSlot }) => {
1995
+ const side = props.side === "end" ? "end" : "start";
1996
+ const wrap = document.createElement("div");
1997
+ wrap.className = `nr-chat nr-chat--${side}`;
1998
+ if (props.class) wrap.classList.add(...props.class.split(/\s+/).filter(Boolean));
1999
+ if (props.style) wrap.setAttribute("style", props.style);
2000
+ const header = document.createElement("div");
2001
+ header.className = "nr-chat__header";
2002
+ if (props.name) {
2003
+ const name = document.createElement("span");
2004
+ name.className = "nr-chat__name";
2005
+ name.textContent = props.name;
2006
+ header.appendChild(name);
2007
+ }
2008
+ if (props.time) {
2009
+ const time = document.createElement("time");
2010
+ time.className = "nr-chat__time";
2011
+ time.textContent = props.time;
2012
+ header.appendChild(time);
2013
+ }
2014
+ if (header.childNodes.length > 0) wrap.appendChild(header);
2015
+ if (props.avatar) {
2016
+ const avatar = document.createElement("div");
2017
+ avatar.className = "nr-chat__avatar";
2018
+ const img = document.createElement("img");
2019
+ img.src = props.avatar;
2020
+ img.alt = props.name || "avatar";
2021
+ avatar.appendChild(img);
2022
+ wrap.appendChild(avatar);
2023
+ }
2024
+ const bubble = document.createElement("div");
2025
+ bubble.className = `nr-chat__bubble${props.color ? ` nr-chat__bubble--${props.color}` : ""}`;
2026
+ bubble.appendChild(renderSlot("default"));
2027
+ wrap.appendChild(bubble);
2028
+ if (props.footer) {
2029
+ const footer = document.createElement("div");
2030
+ footer.className = "nr-chat__footer";
2031
+ footer.textContent = props.footer;
2032
+ wrap.appendChild(footer);
2033
+ }
2034
+ return wrap;
2035
+ };
2036
+ var chatDirective = ({ props, renderSlot }) => {
2037
+ const wrap = document.createElement("div");
2038
+ wrap.className = "nr-chat";
2039
+ if (props.class) wrap.classList.add(...props.class.split(/\s+/).filter(Boolean));
2040
+ if (props.style) wrap.setAttribute("style", props.style);
2041
+ wrap.appendChild(renderSlot("default"));
2042
+ return wrap;
2043
+ };
2044
+ var chat_default = chatDirective;
2045
+
2046
+ // vanilla/directives/events.ts
2047
+ function parseEventProp(eventProp) {
2048
+ if (!eventProp) return [];
2049
+ const bindings = [];
2050
+ for (const part of eventProp.split(";")) {
2051
+ const trimmed = part.trim();
2052
+ if (!trimmed) continue;
2053
+ const idx = trimmed.indexOf(":");
2054
+ if (idx === -1) continue;
2055
+ const eventName = trimmed.slice(0, idx).trim().replace(/^on/i, "");
2056
+ const fnName = trimmed.slice(idx + 1).trim();
2057
+ if (eventName && fnName) bindings.push({ eventName, fnName });
2058
+ }
2059
+ return bindings;
2060
+ }
2061
+ function bindEventProp(el, eventProp) {
2062
+ for (const { eventName, fnName } of parseEventProp(eventProp)) {
2063
+ el.addEventListener(eventName, (e) => {
2064
+ const fn = window[fnName];
2065
+ if (typeof fn === "function") {
2066
+ fn.call(el, e);
2067
+ }
2068
+ });
2069
+ }
2070
+ }
2071
+
2072
+ // vanilla/directives/richlist.ts
2073
+ var richlistItemDirective = ({ props, renderSlot }) => {
2074
+ const li = document.createElement("li");
2075
+ li.className = "nr-richlist__item";
2076
+ if (props.class) li.classList.add(...props.class.split(/\s+/).filter(Boolean));
2077
+ if (props.style) li.setAttribute("style", props.style);
2078
+ if (props.image) {
2079
+ const thumb = document.createElement("div");
2080
+ thumb.className = "nr-richlist__thumb";
2081
+ const img = document.createElement("img");
2082
+ img.src = props.image;
2083
+ img.alt = props.title || "list item";
2084
+ img.loading = "lazy";
2085
+ thumb.appendChild(img);
2086
+ li.appendChild(thumb);
2087
+ }
2088
+ if (props.title || props.subtitle) {
2089
+ const main = document.createElement("div");
2090
+ main.className = "nr-richlist__main";
2091
+ if (props.title) {
2092
+ const title = document.createElement("div");
2093
+ title.className = "nr-richlist__title";
2094
+ title.textContent = props.title;
2095
+ main.appendChild(title);
2096
+ }
2097
+ if (props.subtitle) {
2098
+ const subtitle = document.createElement("div");
2099
+ subtitle.className = "nr-richlist__subtitle";
2100
+ subtitle.textContent = props.subtitle;
2101
+ main.appendChild(subtitle);
2102
+ }
2103
+ li.appendChild(main);
2104
+ }
2105
+ const descFrag = renderSlot("default");
2106
+ if (descFrag.childNodes.length > 0) {
2107
+ const desc = document.createElement("p");
2108
+ desc.className = "nr-richlist__desc";
2109
+ desc.appendChild(descFrag);
2110
+ li.appendChild(desc);
2111
+ }
2112
+ const actions = [
2113
+ { icon: props.icon, url: props.url, event: props.event },
2114
+ { icon: props.icon2, url: props.url2, event: props.event2 }
2115
+ ].filter((a) => !!a.icon);
2116
+ if (actions.length > 0) {
2117
+ const actionsDiv = document.createElement("div");
2118
+ actionsDiv.className = "nr-richlist__actions";
2119
+ for (const { icon, url, event } of actions) {
2120
+ const btn = document.createElement("button");
2121
+ btn.className = "nr-richlist__action";
2122
+ btn.type = "button";
2123
+ btn.setAttribute("aria-label", icon);
2124
+ btn.appendChild(createIcon(icon));
2125
+ if (event) {
2126
+ bindEventProp(btn, event);
2127
+ } else if (url) {
2128
+ btn.addEventListener("click", () => window.open(url, "_blank"));
2129
+ }
2130
+ actionsDiv.appendChild(btn);
2131
+ }
2132
+ li.appendChild(actionsDiv);
2133
+ }
2134
+ return li;
2135
+ };
2136
+ var richlistDirective = ({ props, renderSlot }) => {
2137
+ const ul = document.createElement("ul");
2138
+ ul.className = "nr-richlist";
2139
+ if (props.class) ul.classList.add(...props.class.split(/\s+/).filter(Boolean));
2140
+ if (props.style) ul.setAttribute("style", props.style);
2141
+ ul.appendChild(renderSlot("default"));
2142
+ return ul;
2143
+ };
2144
+ var richlist_default = richlistDirective;
2145
+
2146
+ // vanilla/directives/stat.ts
2147
+ var statDirective = ({ props }) => {
2148
+ const colorClass = props.color ? ` nr-stat--${props.color}` : "";
2149
+ const stat = document.createElement("div");
2150
+ stat.className = `nr-stat${colorClass}`;
2151
+ if (props.class) stat.classList.add(...props.class.split(/\s+/).filter(Boolean));
2152
+ if (props.style) stat.setAttribute("style", props.style);
2153
+ if (props.icon) {
2154
+ const figure = document.createElement("div");
2155
+ figure.className = "nr-stat__figure";
2156
+ figure.appendChild(createIcon(props.icon));
2157
+ stat.appendChild(figure);
2158
+ }
2159
+ if (props.title) {
2160
+ const title = document.createElement("div");
2161
+ title.className = "nr-stat__title";
2162
+ title.textContent = props.title;
2163
+ stat.appendChild(title);
2164
+ }
2165
+ if (props.value) {
2166
+ const value = document.createElement("div");
2167
+ value.className = "nr-stat__value";
2168
+ value.textContent = props.value;
2169
+ stat.appendChild(value);
2170
+ }
2171
+ if (props.desc) {
2172
+ const desc = document.createElement("div");
2173
+ desc.className = "nr-stat__desc";
2174
+ desc.textContent = props.desc;
2175
+ stat.appendChild(desc);
2176
+ }
2177
+ return stat;
2178
+ };
2179
+ var stat_default = statDirective;
2180
+
1634
2181
  // vanilla/directives/index.ts
1635
2182
  var directiveRegistry = {
1636
2183
  // Admonitions
@@ -1653,7 +2200,21 @@ var directiveRegistry = {
1653
2200
  custom: wrapper_default,
1654
2201
  raw: wrapper_default,
1655
2202
  // Animation
1656
- slide: slide_default
2203
+ slide: slide_default,
2204
+ // New components
2205
+ keys: keys_default,
2206
+ accordion: accordion_default,
2207
+ "accordion-item": accordionItemDirective,
2208
+ carousel: carousel_default,
2209
+ countdown: countdown_default,
2210
+ diff: diff_default,
2211
+ "hover-3d": hover3d_default,
2212
+ "hover-gallery": hovergallery_default,
2213
+ chat: chat_default,
2214
+ "chat-item": chatItemDirective,
2215
+ richlist: richlist_default,
2216
+ "richlist-item": richlistItemDirective,
2217
+ stat: stat_default
1657
2218
  };
1658
2219
  var directives_default = directiveRegistry;
1659
2220
 
@@ -1708,11 +2269,18 @@ function renderTokensInner(tokens, ctx) {
1708
2269
  function processElements(elements, ctx, allElements) {
1709
2270
  const fragment = document.createDocumentFragment();
1710
2271
  let i = 0;
2272
+ const BATCHED_DIRECTIVES = {
2273
+ card: "nr-card-grid",
2274
+ "card-m": "nr-card-grid",
2275
+ "card-b": "nr-card-grid",
2276
+ stat: "nr-stat-grid"
2277
+ };
2278
+ const isBatched = (directive) => !!directive && directive.type === "directive" && Object.prototype.hasOwnProperty.call(BATCHED_DIRECTIVES, directive.directiveType);
1711
2279
  while (i < elements.length) {
1712
2280
  const el = elements[i];
1713
- if (el.type === "directive" && ["card", "card-m", "card-b"].includes(el.directiveType)) {
2281
+ if (isBatched(el)) {
1714
2282
  const cards = [];
1715
- while (i < elements.length && elements[i].type === "directive" && ["card", "card-m", "card-b"].includes(elements[i].directiveType)) {
2283
+ while (i < elements.length && isBatched(elements[i])) {
1716
2284
  cards.push(elements[i]);
1717
2285
  i++;
1718
2286
  }
@@ -1723,7 +2291,7 @@ function processElements(elements, ctx, allElements) {
1723
2291
  }
1724
2292
  } else {
1725
2293
  const grid = document.createElement("div");
1726
- grid.className = "nr-card-grid";
2294
+ grid.className = BATCHED_DIRECTIVES[cards[0].directiveType];
1727
2295
  for (const card of cards) {
1728
2296
  const rendered = renderElement(card, ctx, allElements);
1729
2297
  if (rendered) grid.appendChild(rendered);
@@ -1894,8 +2462,393 @@ var CustomMarkdownRenderer = ({ content }) => {
1894
2462
  };
1895
2463
  var CustomMarkdownRenderer_default = CustomMarkdownRenderer;
1896
2464
 
1897
- // react/NReditor.tsx
2465
+ // react/Guide.tsx
2466
+ var import_react4 = require("react");
2467
+
2468
+ // guide/index.ts
2469
+ var guideData = [
2470
+ {
2471
+ "id": "introduccion",
2472
+ "category": "Introducci\xF3n",
2473
+ "title": "Introducci\xF3n",
2474
+ "icon": "menu_book",
2475
+ "order": 1,
2476
+ "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| `![alt](url)` | Imagen |\n| `![alt](url#left)` | 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| `:::div` `:::style` `:::raw` | Layout |\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 contenedores `div`, estilos `style` y HTML `raw`.\n\nCada p\xE1gina incluye: la sintaxis exacta, la tabla de props, un ejemplo en vivo y el c\xF3digo fuente para copiar.'
2477
+ },
2478
+ {
2479
+ "id": "titulos",
2480
+ "category": "Markdown",
2481
+ "title": "T\xEDtulos y encabezados",
2482
+ "icon": "title",
2483
+ "order": 1,
2484
+ "md": "# T\xEDtulos y encabezados\n\nLos t\xEDtulos se escriben con almohadillas `#`. Hay **seis niveles**, de `#` (mayor) a `######` (menor).\n\n## Sintaxis\n\n```md\n# T\xEDtulo 1\n## T\xEDtulo 2\n### T\xEDtulo 3\n#### T\xEDtulo 4\n##### T\xEDtulo 5\n###### T\xEDtulo 6\n```\n\n## Resultado\n\n# T\xEDtulo 1\n## T\xEDtulo 2\n### T\xEDtulo 3\n#### T\xEDtulo 4\n##### T\xEDtulo 5\n###### T\xEDtulo 6\n\n## Notas\n\n- Cada t\xEDtulo genera un **ancla** autom\xE1tica: al pulsar sobre \xE9l se copia el enlace directo a la secci\xF3n.\n- El prefijo `[TOC]` (\xEDndice de contenidos) genera un \xEDndice con todos los t\xEDtulos del documento (ver la p\xE1gina de **Sintaxis inline**).\n- Los t\xEDtulos pueden llevar atributos personalizados con la directiva `:::div` o envolverlos en `:::style` para darles clases o estilos propios.\n\n## Anclas\n\nCualquier t\xEDtulo se puede enlazar con su id autom\xE1tico:\n\n```md\n[Ir a los t\xEDtulos](#t\xEDtulos-y-encabezados)\n```\n\n> El id se genera a partir del texto del t\xEDtulo, en min\xFAsculas y con guiones."
2485
+ },
2486
+ {
2487
+ "id": "enfasis",
2488
+ "category": "Markdown",
2489
+ "title": "\xC9nfasis, c\xF3digo y enlaces",
2490
+ "icon": "format_bold",
2491
+ "order": 2,
2492
+ "md": '# \xC9nfasis, c\xF3digo y enlaces\n\n## \xC9nfasis b\xE1sico\n\n| Sintaxis | Resultado |\n| --- | --- |\n| `**negrita**` | **negrita** |\n| `*cursiva*` o `_cursiva_` | *cursiva* |\n| `~~tachado~~` | ~~tachado~~ |\n| `***negrita y cursiva***` | ***negrita y cursiva*** |\n\n## C\xF3digo inline\n\nEl c\xF3digo en l\xEDnea se escribe entre comillas invertidas:\n\n```md\nUsa la funci\xF3n `renderMarkdownString()` para renderizar.\n```\n\nUsa la funci\xF3n `renderMarkdownString()` para renderizar.\n\n## Enlaces\n\n### Externos\n\n```md\n[Ir a la documentaci\xF3n](https://example.com)\n```\n\n[Ir a la documentaci\xF3n](https://example.com)\n\n### Anclas internas\n\n```md\n[Volver al inicio](#introducci\xF3n-a-noirmd)\n```\n\n[Volver al inicio](#introducci\xF3n-a-noirmd)\n\n### Enlace con t\xEDtulo\n\n```md\n[Pasa el rat\xF3n aqu\xED](https://example.com "T\xEDtulo del enlace")\n```\n\n[Pasa el rat\xF3n aqu\xED](https://example.com "T\xEDtulo del enlace")\n\n## P\xE1rrafos y saltos de l\xEDnea\n\n- Un salto de l\xEDnea simple **no** separa p\xE1rrafos; se necesita una l\xEDnea en blanco.\n- Para un salto de l\xEDnea forzado, termina la l\xEDnea con dos espacios o usa `\\`.\n\n```md\nPrimer p\xE1rrafo.\n\nSegundo p\xE1rrafo con salto forzado \ny esta l\xEDnea debajo.\n```\n\n> Los p\xE1rrafos vac\xEDos entre bloques se eliminan autom\xE1ticamente para no dejar huecos.'
2493
+ },
2494
+ {
2495
+ "id": "listas",
2496
+ "category": "Markdown",
2497
+ "title": "Listas",
2498
+ "icon": "format_list_bulleted",
2499
+ "order": 3,
2500
+ "md": "# Listas\n\n## Lista desordenada\n\n```md\n- Elemento uno\n- Elemento dos\n- Elemento tres\n```\n\n- Elemento uno\n- Elemento dos\n- Elemento tres\n\n## Lista ordenada\n\n```md\n1. Primer paso\n2. Segundo paso\n3. Tercer paso\n```\n\n1. Primer paso\n2. Segundo paso\n3. Tercer paso\n\n## Listas anidadas\n\n```md\n- Frutas\n - Manzana\n - Pera\n- Verduras\n 1. Zanahoria\n 2. Calabac\xEDn\n- Legumbres\n```\n\n- Frutas\n - Manzana\n - Pera\n- Verduras\n 1. Zanahoria\n 2. Calabac\xEDn\n- Legumbres\n\n## Listas mixtas\n\n```md\n- Paso uno: preparar\n 1. Cortar\n 2. Pelar\n- Paso dos: cocinar\n - Hervir\n - Saltear\n```\n\n- Paso uno: preparar\n 1. Cortar\n 2. Pelar\n- Paso dos: cocinar\n - Hervir\n - Saltear\n\n## Notas\n\n- La anidaci\xF3n se hace con **4 espacios** (o un tabulador).\n- Los elementos de lista admiten todo el markdown: `**negrita**`, `[enlaces](#listas)`, c\xF3digo `` `inline` `` e incluso admoniciones y directivas anidadas.\n- Si un elemento contiene un p\xE1rrafo completo, a\xF1ade una l\xEDnea en blanco dentro del elemento."
2501
+ },
2502
+ {
2503
+ "id": "tablas",
2504
+ "category": "Markdown",
2505
+ "title": "Tablas",
2506
+ "icon": "table_chart",
2507
+ "order": 4,
2508
+ "md": "# Tablas\n\nLas tablas usan la sintaxis de tuber\xEDas `|`. La segunda fila define la alineaci\xF3n.\n\n## Sintaxis\n\n```md\n| Columna A | Columna B | Columna C |\n| --- | --- | --- |\n| a1 | b1 | c1 |\n| a2 | b2 | c2 |\n```\n\n| Columna A | Columna B | Columna C |\n| --- | --- | --- |\n| a1 | b1 | c1 |\n| a2 | b2 | c2 |\n\n## Alineaci\xF3n\n\nUsa dos puntos en la fila de separaci\xF3n para alinear columnas:\n\n```md\n| Izquierda | Centro | Derecha |\n| :--- | :---: | ---: |\n| texto | texto | texto |\n```\n\n| Izquierda | Centro | Derecha |\n| :--- | :---: | ---: |\n| texto | texto | texto |\n\n## Contenido enriquecido\n\nLas celdas admiten **negrita**, *cursiva*, `` `c\xF3digo` `` y enlaces:\n\n| Directiva | Props | Descripci\xF3n |\n| :--- | :--- | :--- |\n| `:::card` | `title`, `icon` | Tarjeta de contenido |\n| `:::chat` | `side`, `name` | Burbuja de chat |\n| `:::stat` | `value`, `color` | Estad\xEDstica |\n\n## Escapar la tuber\xEDa\n\nSi un valor contiene una `|`, esc\xE1pala con barra invertida:\n\n```md\n| Prop | Valores |\n| --- | --- |\n| float | `left` \\| `right` \\| `center` |\n```\n\n| Prop | Valores |\n| --- | --- |\n| float | `left` \\| `right` \\| `center` |\n\n> Las tablas con muchas columnas se vuelven horizontales en pantallas peque\xF1as."
2509
+ },
2510
+ {
2511
+ "id": "codigo",
2512
+ "category": "Markdown",
2513
+ "title": "Bloques de c\xF3digo",
2514
+ "icon": "code",
2515
+ "order": 5,
2516
+ "md": "# Bloques de c\xF3digo\n\nLos bloques de c\xF3digo se escriben con **tres comillas invertidas** (o tres tildes `~~~`). El lenguaje opcional activa el resaltado de sintaxis.\n\n## Sintaxis\n\n````md\n```js\nconst saludo = (nombre) => `Hola, ${nombre}!`;\nconsole.log(saludo('mundo'));\n```\n````\n\n```js\nconst saludo = (nombre) => `Hola, ${nombre}!`;\nconsole.log(saludo('mundo'));\n```\n\n## Lenguajes soportados\n\nEl resaltado funciona con los lenguajes de **highlight.js**: `js`, `ts`, `json`, `html`, `css`, `python`, `bash`, `md`, `sql`, `java`, `c`, `cpp`, `rust`, `go`, etc.\n\n```python\ndef factorial(n):\n return 1 if n <= 1 else n * factorial(n - 1)\n\nprint(factorial(5))\n```\n\n```css\n.nr-guide__panel {\n display: flex;\n border-radius: 14px;\n overflow: hidden;\n}\n```\n\n## Notas\n\n- Los bloques de c\xF3digo **no** procesan directivas: lo que escribas dentro se muestra literal.\n- El bot\xF3n de copiar (esquina superior derecha) copia el contenido al portapapeles.\n- Los bloques largos se desplazan verticalmente; no rompen el layout.\n\n## Mostrar la sintaxis de directivas\n\nPara ense\xF1ar directivas dentro de la propia gu\xEDa se usa un bloque con lenguaje `md`:\n\n```md\n:::card {title=\"Ejemplo\"}\n\nContenido de la tarjeta.\n\n:::\n```\n\n> El lenguaje `md` (o `markdown`) resalta la sintaxis de directivas de NoirMD."
2517
+ },
2518
+ {
2519
+ "id": "imagenes",
2520
+ "category": "Markdown",
2521
+ "title": "Im\xE1genes",
2522
+ "icon": "image",
2523
+ "order": 6,
2524
+ "md": "# Im\xE1genes\n\n## B\xE1sica\n\n```md\n![Monta\xF1as](https://img.daisyui.com/images/stock/photo-1506905925346-21bda4d32df4.webp)\n```\n\n![Monta\xF1as](https://img.daisyui.com/images/stock/photo-1506905925346-21bda4d32df4.webp)\n\n## Con tama\xF1o\n\nA\xF1ade el tama\xF1o entre llaves despu\xE9s de la URL en formato `ancho:alto` (px):\n\n```md\n![Monta\xF1as](https://img.daisyui.com/images/stock/photo-1506905925346-21bda4d32df4.webp){300:200}\n```\n\n![Monta\xF1as](https://img.daisyui.com/images/stock/photo-1506905925346-21bda4d32df4.webp){300:200}\n\n## Flotante\n\nEl sufijo `#left`, `#right` o `#center` despu\xE9s de la URL hace la imagen flotante (el texto la rodea):\n\n```md\n![R\xEDo](https://img.daisyui.com/images/stock/photo-1470252649378-9c29740c9fa8.webp#left){220:150}\n\nTexto que fluye alrededor de la imagen flotante...\n```\n\n![R\xEDo](https://img.daisyui.com/images/stock/photo-1470252649378-9c29740c9fa8.webp#left){220:150}\n\nTexto que fluye alrededor de la imagen flotante a la izquierda: el p\xE1rrafo siguiente acompa\xF1a la imagen sin romper la l\xEDnea. Puedes usar `#right` para colocarla a la derecha y `#center` para centrarla (en ese caso conviene definir tama\xF1o).\n\n### Derecha\n\n```md\n![Bosque](https://img.daisyui.com/images/stock/photo-1470071459604-3b5ec3a7fe05.webp#right){200:130}\n```\n\n![Bosque](https://img.daisyui.com/images/stock/photo-1470071459604-3b5ec3a7fe05.webp#right){200:130}\n\n### Centrada\n\n```md\n![Paisaje](https://img.daisyui.com/images/stock/photo-1441974231531-c6227db76b6e.webp#center){400:220}\n```\n\n![Paisaje](https://img.daisyui.com/images/stock/photo-1441974231531-c6227db76b6e.webp#center){400:220}\n\n## Notas\n\n- Sin `{w:h}`, la imagen respeta su tama\xF1o natural (m\xE1ximo el ancho del contenedor).\n- Las im\xE1genes flotantes sin tama\xF1o usan un ancho m\xE1ximo del 50%.\n- Para comparar dos im\xE1genes con un slider, usa la directiva `:::diff` (ver **Componentes**)."
2525
+ },
2526
+ {
2527
+ "id": "inline",
2528
+ "category": "Markdown",
2529
+ "title": "Sintaxis inline",
2530
+ "icon": "text_fields",
2531
+ "order": 7,
2532
+ "md": "# Sintaxis inline\n\nAdem\xE1s del markdown cl\xE1sico, NoirMD a\xF1ade estilos inline propios.\n\n## Resaltado\n\n```md\n==Este texto est\xE1 resaltado==\n```\n\n==Este texto est\xE1 resaltado==\n\n## Texto de color\n\nSe usa `%color%texto%%`:\n\n```md\n%red%texto rojo%% %green%texto verde%% %blue%texto azul%%\n```\n\n%red%texto rojo%% %green%texto verde%% %blue%texto azul%%\n\n| Color | Ejemplo |\n| --- | --- |\n| `%red%` | %red%rojo%% |\n| `%green%` | %green%verde%% |\n| `%blue%` | %blue%azul%% |\n| `%yellow%` | %yellow%amarillo%% |\n| `%orange%` | %orange%naranja%% |\n| `%purple%` | %purple%morado%% |\n| `%cyan%` | %cyan%cian%% |\n| `%pink%` | %pink%rosa%% |\n\n## Subrayado\n\n```md\n!~subrayado~!\n```\n\n!~subrayado~!\n\n## Spoiler\n\n```md\n!>El final de la pel\xEDcula era un sue\xF1o<!\n```\n\n!>El final de la pel\xEDcula era un sue\xF1o<!\n\n> Pasa el rat\xF3n (o pulsa) sobre el texto oculto para revelarlo.\n\n## Centrado y derecha\n\n```md\n->texto centrado<-\n->texto a la derecha->\n```\n\n->texto centrado<-\n\n->texto a la derecha->\n\n## Iconos Material\n\nLos iconos se insertan con el nombre entre `|[[ ]]|`:\n\n```md\n|[[favorite]]| Me gusta |[[send]]| Enviar |[[star]]| Destacar\n```\n\n|[[favorite]]| Me gusta |[[send]]| Enviar |[[star]]| Destacar\n\n> Usa cualquier nombre de la colecci\xF3n [Material Symbols](https://fonts.google.com/icons). Tambi\xE9n funcionan en props como `icon` de las directivas.\n\n## \xCDndice de contenidos\n\n```md\n[TOC]\n```\n\n[TOC]\n\n> El `[TOC]` genera un \xEDndice clicable con todos los t\xEDtulos del documento. Tambi\xE9n existe como prop `toc` del editor."
2533
+ },
2534
+ {
2535
+ "id": "nota",
2536
+ "category": "Admoniciones",
2537
+ "title": "Nota",
2538
+ "icon": "sticky_note_2",
2539
+ "order": 1,
2540
+ "md": '# Admonici\xF3n: Nota\n\nLa directiva `:::note` crea una caja de aviso neutra, \xFAtil para informaci\xF3n complementaria.\n\n## Sintaxis\n\n```md\n:::note\nTexto de la nota.\n:::\n```\n\n:::note\nTexto de la nota.\n:::\n\n## Con t\xEDtulo\n\n```md\n:::note Recuerda\nGuarda tu trabajo con `Ctrl+S` antes de salir.\n:::\n```\n\n:::note Recuerda\nGuarda tu trabajo con `Ctrl+S` antes de salir.\n:::\n\n## Props\n\n| Prop | Tipo | Descripci\xF3n |\n| --- | --- | --- |\n| `title` | texto | T\xEDtulo del bloque (sin `:` en el nombre, es el texto tras `note`) |\n| `icon` | nombre Material | Icono personalizado del bloque |\n| `class` | texto | Clases CSS adicionales |\n| `style` | CSS | Estilos inline |\n\n## Anidando directivas\n\nLas admoniciones admiten markdown completo dentro:\n\n```md\n:::note Propina\nPuedes anidar **directivas** dentro de una nota:\n- `:::keys` para atajos\n- Tablas, c\xF3digo, enlaces...\n:::\n```\n\n:::note Propina\nPuedes anidar **directivas** dentro de una nota:\n\n:::keys {size="sm"}\nCTRL + S\n:::\n\n- Tablas, c\xF3digo, enlaces...\n:::'
2541
+ },
2542
+ {
2543
+ "id": "warning",
2544
+ "category": "Admoniciones",
2545
+ "title": "Warning",
2546
+ "icon": "warning_amber",
2547
+ "order": 2,
2548
+ "md": '# Admonici\xF3n: Warning\n\nLa directiva `:::warning` crea una caja de aviso \xE1mbar, para precauciones o advertencias moderadas.\n\n## Sintaxis\n\n```md\n:::warning\nCuidado con esto.\n:::\n```\n\n:::warning\nCuidado con esto.\n:::\n\n## Con t\xEDtulo e icono\n\n```md\n:::warning {title="Precauci\xF3n" icon="warning"}\nEl bloque `:::` debe cerrarse correctamente.\n:::\n```\n\n:::warning {title="Precauci\xF3n" icon="warning"}\nEl bloque `:::` debe cerrarse correctamente.\n:::\n\n## Props\n\n| Prop | Tipo | Descripci\xF3n |\n| --- | --- | --- |\n| `title` | texto | T\xEDtulo del bloque |\n| `icon` | nombre Material | Icono personalizado |\n| `class` | texto | Clases CSS adicionales |\n| `style` | CSS | Estilos inline |\n\n## Combinando admoniciones\n\n```md\n:::warning Contenido sensible\nEsta parte **se borra** al vaciar el editor:\n- No guardes aqu\xED contrase\xF1as\n- Usa `:::danger` para lo cr\xEDtico\n:::\n```\n\n:::warning Contenido sensible\nEsta parte **se borra** al vaciar el editor:\n\n- No guardes aqu\xED contrase\xF1as\n- Usa `:::danger` para lo cr\xEDtico\n:::'
2549
+ },
2550
+ {
2551
+ "id": "danger",
2552
+ "category": "Admoniciones",
2553
+ "title": "Danger",
2554
+ "icon": "error",
2555
+ "order": 3,
2556
+ "md": '# Admonici\xF3n: Danger\n\nLa directiva `:::danger` crea una caja roja de error o peligro, para lo m\xE1s cr\xEDtico.\n\n## Sintaxis\n\n```md\n:::danger\n\xA1Esto puede romper tu documento!\n:::\n```\n\n:::danger\n\xA1Esto puede romper tu documento!\n:::\n\n## Con t\xEDtulo\n\n```md\n:::danger {title="Error irrecuperable" icon="error"}\nLa variable `{{title}}` no existe.\n:::\n```\n\n:::danger {title="Error irrecuperable" icon="error"}\nLa variable `{{title}}` no existe.\n:::\n\n## Props\n\n| Prop | Tipo | Descripci\xF3n |\n| --- | --- | --- |\n| `title` | texto | T\xEDtulo del bloque |\n| `icon` | nombre Material | Icono personalizado |\n| `class` | texto | Clases CSS adicionales |\n| `style` | CSS | Estilos inline |\n\n## Uso combinado con c\xF3digo\n\n````md\n:::danger Tiempo agotado\nLa sesi\xF3n expir\xF3. Vuelve a iniciar sesi\xF3n:\n\n```md\n[Iniciar sesi\xF3n](/login)\n```\n:::\n````\n\n:::danger Tiempo agotado\nLa sesi\xF3n expir\xF3. Vuelve a iniciar sesi\xF3n:\n\n[Iniciar sesi\xF3n](/login)\n:::'
2557
+ },
2558
+ {
2559
+ "id": "info",
2560
+ "category": "Admoniciones",
2561
+ "title": "Info",
2562
+ "icon": "info",
2563
+ "order": 4,
2564
+ "md": '# Admonici\xF3n: Info\n\nLa directiva `:::info` crea una caja azul de informaci\xF3n t\xE9cnica o contextual.\n\n## Sintaxis\n\n```md\n:::info\nDato t\xE9cnico o contextual.\n:::\n```\n\n:::info\nDato t\xE9cnico o contextual.\n:::\n\n## Con t\xEDtulo\n\n```md\n:::info {title="API" icon="api"}\nEl endpoint devuelve `application/json`.\n:::\n```\n\n:::info {title="API" icon="api"}\nEl endpoint devuelve `application/json`.\n:::\n\n## Props\n\n| Prop | Tipo | Descripci\xF3n |\n| --- | --- | --- |\n| `title` | texto | T\xEDtulo del bloque |\n| `icon` | nombre Material | Icono personalizado |\n| `class` | texto | Clases CSS adicionales |\n| `style` | CSS | Estilos inline |\n\n## Ejemplo con tablas\n\n```md\n:::info Versiones\n| Versi\xF3n | Estado |\n| --- | --- |\n| v2.0 | Estable |\n| v3.0 | Beta |\n:::\n```\n\n:::info Versiones\n| Versi\xF3n | Estado |\n| --- | --- |\n| v2.0 | Estable |\n| v3.0 | Beta |\n:::'
2565
+ },
2566
+ {
2567
+ "id": "greentext",
2568
+ "category": "Admoniciones",
2569
+ "title": "Greentext",
2570
+ "icon": "chat",
2571
+ "order": 5,
2572
+ "md": '# Admonici\xF3n: Greentext\n\nLa directiva `:::greentext` crea un bloque verde tipo foro, para citas informales, humor o contexto narrativo.\n\n## Sintaxis\n\n```md\n:::greentext\n> el usuario que usa markdown simple\n> no conoce el poder de las directivas\n:::\n```\n\n:::greentext\n> el usuario que usa markdown simple\n> no conoce el poder de las directivas\n:::\n\n## Con t\xEDtulo\n\n```md\n:::greentext {title="Feedback del usuario"}\n> la gu\xEDa escribe sola\n> 10/10\n:::\n```\n\n:::greentext {title="Feedback del usuario"}\n> la gu\xEDa escribe sola\n> 10/10\n:::\n\n## Props\n\n| Prop | Tipo | Descripci\xF3n |\n| --- | --- | --- |\n| `title` | texto | T\xEDtulo del bloque |\n| `icon` | nombre Material | Icono personalizado |\n| `class` | texto | Clases CSS adicionales |\n| `style` | CSS | Estilos inline |\n\n## Combinando estilos\n\n```md\n:::greentext\n> **%green%alguien%%**: \xBFy si anidamos una nota?\n> **%green%otro%%**: `:::note` funciona dentro\n:::\n```\n\n:::greentext\n> **%green%alguien%%**: \xBFy si anidamos una nota?\n> **%green%otro%%**: `:::note` funciona dentro\n:::'
2573
+ },
2574
+ {
2575
+ "id": "card",
2576
+ "category": "Componentes",
2577
+ "title": "Card",
2578
+ "icon": "dashboard",
2579
+ "order": 1,
2580
+ "md": '# Card\n\nLa directiva `:::card` crea una tarjeta con icono, t\xEDtulo y contenido markdown.\n\n## Sintaxis\n\n```md\n:::card {title="Tarjeta simple" icon="star"}\nContenido de la tarjeta en **markdown**.\n:::\n```\n\n:::card {title="Tarjeta simple" icon="star"}\nContenido de la tarjeta en **markdown**.\n:::\n\n## Con t\xEDtulo largo y contenido enriquecido\n\n```md\n:::card {title="Documentaci\xF3n t\xE9cnica" icon="code"}\n- Renderizado por el mismo motor\n- Soporta `inline`, tablas y directivas\n- Sin t\xEDtulo: usa `:::card` a secas\n:::\n```\n\n:::card {title="Documentaci\xF3n t\xE9cnica" icon="code"}\n- Renderizado por el mismo motor\n- Soporta `inline`, tablas y directivas\n- Sin t\xEDtulo: usa `:::card` a secas\n:::\n\n## Grid autom\xE1tico\n\nLas tarjetas **consecutivas** se agrupan en una cuadr\xEDcula responsive. A\xF1ade `batch="off"` para evitarlo:\n\n```md\n:::card {title="HTML" icon="html"}\nEstructura del documento.\n:::\n:::card {title="CSS" icon="palette"}\nEstilos y variables.\n:::\n:::card {title="JS" icon="javascript"}\nInteracci\xF3n y eventos.\n:::\n```\n\n:::card {title="HTML" icon="html"}\nEstructura del documento.\n:::\n:::card {title="CSS" icon="palette"}\nEstilos y variables.\n:::\n:::card {title="JS" icon="javascript"}\nInteracci\xF3n y eventos.\n:::\n\n## Props\n\n| Prop | Tipo | Descripci\xF3n |\n| --- | --- | --- |\n| `title` | texto | T\xEDtulo de la tarjeta |\n| `icon` | nombre Material | Icono del t\xEDtulo |\n| `batch` | `off` | Desactiva el agrupado en grid con las tarjetas vecinas |\n| `class` | texto | Clases CSS adicionales |\n| `style` | CSS | Estilos inline |\n\n## Anidando directivas\n\n```md\n:::card {title="Ejemplo anidado" icon="layers"}\nUna admonici\xF3n dentro de la tarjeta:\n\n:::note\nLas tarjetas aceptan cualquier directiva dentro.\n:::\n:::\n```\n\n:::card {title="Ejemplo anidado" icon="layers"}\nUna admonici\xF3n dentro de la tarjeta:\n\n:::note\nLas tarjetas aceptan cualquier directiva dentro.\n:::\n:::'
2581
+ },
2582
+ {
2583
+ "id": "keys",
2584
+ "category": "Componentes",
2585
+ "title": "Keys (teclas)",
2586
+ "icon": "keyboard",
2587
+ "order": 2,
2588
+ "md": '# Keys (teclas)\n\nLa directiva `:::keys` muestra combinaciones de teclado con apariencia de teclas f\xEDsicas.\n\n## Sintaxis\n\n```md\n:::keys\nCTRL + C\n:::\n```\n\n:::keys\nCTRL + C\n:::\n\n## Varias combinaciones\n\n```md\n:::keys\nCTRL + SHIFT + P\n:::\n```\n\n:::keys\nCTRL + SHIFT + P\n:::\n\n:::keys\nALT + F4\n:::\n\n:::keys\nESC\n:::\n\n## Tama\xF1os\n\n| Tama\xF1o | Sintaxis |\n| --- | --- |\n| `xs` | `:::keys {size="xs"}` |\n| `sm` | `:::keys {size="sm"}` |\n| `md` | sin prop (default) |\n| `lg` | `:::keys {size="lg"}` |\n| `xl` | `:::keys {size="xl"}` |\n\n:::keys {size="xs"}\nCTRL + A\n:::\n\n:::keys {size="sm"}\nCTRL + A\n:::\n\n:::keys {size="md"}\nCTRL + A\n:::\n\n:::keys {size="lg"}\nCTRL + A\n:::\n\n:::keys {size="xl"}\nCTRL + A\n:::\n\n## Props\n\n| Prop | Tipo | Descripci\xF3n |\n| --- | --- | --- |\n| `size` | `xs` \\| `sm` \\| `md` \\| `lg` \\| `xl` | Tama\xF1o de las teclas (default `md`) |\n| `class` | texto | Clases CSS adicionales |\n| `style` | CSS | Estilos inline |\n\n## Ejemplo combinado\n\n```md\n:::keys {size="lg"}\nSHIFT + CTRL + G\n:::\n```\n\n:::keys {size="lg"}\nSHIFT + CTRL + G\n:::\n\n> Las teclas se separan autom\xE1ticamente por el signo `+`.'
2589
+ },
2590
+ {
2591
+ "id": "accordion",
2592
+ "category": "Componentes",
2593
+ "title": "Accordion",
2594
+ "icon": "unfold_more",
2595
+ "order": 3,
2596
+ "md": '# Accordion\n\nLa directiva `:::accordion` agrupa items desplegables (`:::accordion-item`). Por defecto solo un item puede estar abierto (modo `radio`).\n\n## Sintaxis\n\n```md\n:::accordion\n:::accordion-item {title="Primera secci\xF3n"}\nContenido de la primera secci\xF3n.\n:::\n:::accordion-item {title="Segunda secci\xF3n"}\nContenido de la segunda secci\xF3n.\n:::\n:::\n```\n\n:::accordion\n:::accordion-item {title="Primera secci\xF3n"}\nContenido de la primera secci\xF3n.\n:::\n:::accordion-item {title="Segunda secci\xF3n"}\nContenido de la segunda secci\xF3n.\n:::\n:::\n\n## Abierto por defecto\n\nCon `checked` el item nace abierto:\n\n```md\n:::accordion\n:::accordion-item {title="FAQ: \xBFQu\xE9 es NoirMD?" checked}\nUn editor markdown con directivas propias.\n:::\n:::accordion-item {title="FAQ: \xBFC\xF3mo anido directivas?"}\nDentro del contenido de cualquier item puedes usar `:::`.\n:::\n:::\n```\n\n:::accordion\n:::accordion-item {title="FAQ: \xBFQu\xE9 es NoirMD?" checked}\nUn editor markdown con directivas propias.\n:::\n:::accordion-item {title="FAQ: \xBFC\xF3mo anido directivas?"}\nDentro del contenido de cualquier item puedes usar `:::`.\n:::\n:::\n\n## Modo checkbox (multiples abiertos)\n\n```md\n:::accordion {mode="checkbox"}\n:::accordion-item {title="Paso 1" checked}\nPreparar los ingredientes.\n:::\n:::accordion-item {title="Paso 2"}\nMezclar y hornear.\n:::\n:::\n```\n\n:::accordion {mode="checkbox"}\n:::accordion-item {title="Paso 1" checked}\nPreparar los ingredientes.\n:::\n:::accordion-item {title="Paso 2"}\nMezclar y hornear.\n:::\n:::\n\n## Props\n\n### `:::accordion`\n\n| Prop | Tipo | Descripci\xF3n |\n| --- | --- | --- |\n| `mode` | `radio` \\| `checkbox` | `radio` (default): solo uno abierto; `checkbox`: varios |\n| `class` | texto | Clases CSS adicionales |\n| `style` | CSS | Estilos inline |\n\n### `:::accordion-item`\n\n| Prop | Tipo | Descripci\xF3n |\n| --- | --- | --- |\n| `title` | texto | T\xEDtulo del item (requerido) |\n| `checked` | flag | Item abierto por defecto |\n| `value` | texto | Valor asociado al input |\n| `class` | texto | Clases CSS adicionales |\n| `style` | CSS | Estilos inline |'
2597
+ },
2598
+ {
2599
+ "id": "carousel",
2600
+ "category": "Componentes",
2601
+ "title": "Carousel",
2602
+ "icon": "view_carousel",
2603
+ "order": 4,
2604
+ "md": '# Carousel\n\nLa directiva `:::carousel` muestra un carrusel de im\xE1genes con flechas, puntos de navegaci\xF3n y **loop infinito**.\n\n## Sintaxis\n\nLas im\xE1genes se ponen como markdown dentro del bloque:\n\n```md\n:::carousel {height="320px"}\n![Foto 1](https://img.daisyui.com/images/stock/photo-1506905925346-21bda4d32df4.webp)\n![Foto 2](https://img.daisyui.com/images/stock/photo-1470252649378-9c29740c9fa8.webp)\n![Foto 3](https://img.daisyui.com/images/stock/photo-1441974231531-c6227db76b6e.webp)\n![Foto 4](https://img.daisyui.com/images/stock/photo-1500530855697-b586d89ba3ee.webp)\n:::\n```\n\n:::carousel {height="320px"}\n![Foto 1](https://img.daisyui.com/images/stock/photo-1506905925346-21bda4d32df4.webp)\n![Foto 2](https://img.daisyui.com/images/stock/photo-1470252649378-9c29740c9fa8.webp)\n![Foto 3](https://img.daisyui.com/images/stock/photo-1441974231531-c6227db76b6e.webp)\n![Foto 4](https://img.daisyui.com/images/stock/photo-1500530855697-b586d89ba3ee.webp)\n:::\n\n## Tama\xF1o y proporci\xF3n\n\n- Sin props, el viewport usa `16/9` de aspecto.\n- `height` fija la altura del viewport (las im\xE1genes lo rellenan).\n- `aspect` fija la proporci\xF3n (`4/3`, `1/1`, `21/9`...).\n\n```md\n:::carousel {aspect="4/3" width="420px" float="right"}\n![A](https://img.daisyui.com/images/stock/photo-1529626455594-4ff0802cfb7e.webp)\n![B](https://img.daisyui.com/images/stock/photo-1534528741775-53994a69daeb.webp)\n:::\n```\n\n:::carousel {aspect="4/3" width="420px" float="right"}\n![A](https://img.daisyui.com/images/stock/photo-1529626455594-4ff0802cfb7e.webp)\n![B](https://img.daisyui.com/images/stock/photo-1534528741775-53994a69daeb.webp)\n:::\n\n## Props\n\n| Prop | Tipo | Descripci\xF3n |\n| --- | --- | --- |\n| `height` | CSS (px) | Altura fija del viewport |\n| `aspect` | ratio | Proporci\xF3n del viewport (default `16/9`) |\n| `width` | CSS (px, %) | Ancho del carrusel |\n| `float` | `left` \\| `right` \\| `center` | Flotaci\xF3n (sin `width`, el flotante usa `max-width: 50%`) |\n| `class` | texto | Clases CSS adicionales |\n| `style` | CSS | Estilos inline |\n\n## Interacci\xF3n\n\n- **Flechas** (izquierda/derecha): navegar.\n- **Puntos** inferiores: ir a una imagen.\n- **Teclado**: `\u2190` y `\u2192` cuando el carrusel est\xE1 enfocado.\n- El carrusel **da la vuelta** al llegar al final (loop infinito).\n\n> Las im\xE1genes se recortan (`object-fit: cover`) para rellenar el viewport sin deformarse.'
2605
+ },
2606
+ {
2607
+ "id": "countdown",
2608
+ "category": "Componentes",
2609
+ "title": "Countdown",
2610
+ "icon": "timer",
2611
+ "order": 5,
2612
+ "md": '# Countdown\n\nLa directiva `:::countdown` muestra una cuenta atr\xE1s en tiempo real.\n\n## Cuenta atr\xE1s fija\n\nCon `days`, `hours`, `min` y `sec` se define una duraci\xF3n que se va agotando:\n\n```md\n:::countdown {days="0" hours="0" min="2" sec="30"}\n:::\n```\n\n:::countdown {days="0" hours="0" min="2" sec="30"}\n:::\n\n## Cuenta atr\xE1s a una fecha (live)\n\nCon `target` el contador cuenta hasta una fecha concreta (`YYYY-MM-DDTHH:mm:ss`):\n\n```md\n:::countdown {target="2027-01-01T00:00:00"}\n:::\n```\n\n:::countdown {target="2027-01-01T00:00:00"}\n:::\n\n## Con etiquetas personalizadas\n\n```md\n:::countdown {days="1" hours="4" min="12" sec="45" labels="D\xEDas|Horas|Min|Seg"}\n:::\n```\n\n:::countdown {days="1" hours="4" min="12" sec="45" labels="D\xEDas|Horas|Min|Seg"}\n:::\n\n## Props\n\n| Prop | Tipo | Descripci\xF3n |\n| --- | --- | --- |\n| `days` | n\xFAmero | D\xEDas de duraci\xF3n |\n| `hours` | n\xFAmero | Horas de duraci\xF3n |\n| `min` | n\xFAmero | Minutos de duraci\xF3n |\n| `sec` | n\xFAmero | Segundos de duraci\xF3n |\n| `target` | fecha ISO | Fecha objetivo (cuenta hacia ella) |\n| `labels` | texto `\\|` | Etiquetas bajo los d\xEDgitos, separadas por `\\|` |\n| `digits` | n\xFAmero | D\xEDgitos por bloque (default `2`) |\n| `class` | texto | Clases CSS adicionales |\n| `style` | CSS | Estilos inline |\n\n> Puedes combinar `target` con `labels` para una cuenta atr\xE1s de evento completa. Al llegar a cero se muestra `00:00:00:00`.'
2613
+ },
2614
+ {
2615
+ "id": "diff",
2616
+ "category": "Componentes",
2617
+ "title": "Diff (comparar im\xE1genes)",
2618
+ "icon": "compare",
2619
+ "order": 6,
2620
+ "md": '# Diff (comparar im\xE1genes)\n\nLa directiva `:::diff` muestra **antes y despu\xE9s** con un slider arrastrable.\n\n## Sintaxis\n\nSe indican las dos im\xE1genes con las props `before` y `after`:\n\n```md\n:::diff {before="https://img.daisyui.com/images/stock/photo-1565098772267-60af42b81ef2.webp" after="https://img.daisyui.com/images/stock/photo-1572635196237-14b3f281503f.webp"}\n:::\n```\n\n:::diff {before="https://img.daisyui.com/images/stock/photo-1565098772267-60af42b81ef2.webp" after="https://img.daisyui.com/images/stock/photo-1572635196237-14b3f281503f.webp"}\n:::\n\n## Con dos im\xE1genes markdown\n\nAlternativa: dos im\xE1genes en el cuerpo del bloque (la primera es el \xABantes\xBB):\n\n```md\n:::diff {height="320px"}\n![Antes](https://img.daisyui.com/images/stock/photo-1565098772267-60af42b81ef2.webp)\n![Despu\xE9s](https://img.daisyui.com/images/stock/photo-1572635196237-14b3f281503f.webp)\n:::\n```\n\n:::diff {height="320px"}\n![Antes](https://img.daisyui.com/images/stock/photo-1565098772267-60af42b81ef2.webp)\n![Despu\xE9s](https://img.daisyui.com/images/stock/photo-1572635196237-14b3f281503f.webp)\n:::\n\n## Tama\xF1o y flotaci\xF3n\n\n```md\n:::diff {width="440px" aspect="4/3" float="left" before="https://img.daisyui.com/images/stock/photo-1565098772267-60af42b81ef2.webp" after="https://img.daisyui.com/images/stock/photo-1572635196237-14b3f281503f.webp"}\n:::\n```\n\n:::diff {width="440px" aspect="4/3" float="left" before="https://img.daisyui.com/images/stock/photo-1565098772267-60af42b81ef2.webp" after="https://img.daisyui.com/images/stock/photo-1572635196237-14b3f281503f.webp"}\n:::\n\nTexto que fluye junto al diff flotante: la comparaci\xF3n queda integrada en el p\xE1rrafo como una imagen flotante m\xE1s.\n\n## Props\n\n| Prop | Tipo | Descripci\xF3n |\n| --- | --- | --- |\n| `before` | URL | Imagen \xABantes\xBB (descartada si hay dos im\xE1genes markdown) |\n| `after` | URL | Imagen \xABdespu\xE9s\xBB |\n| `height` | CSS (px) | Altura del comparador (default `16/9` de aspecto) |\n| `aspect` | ratio | Proporci\xF3n (`4/3`, `1/1`, ...) |\n| `width` | CSS (px, %) | Ancho del comparador |\n| `float` | `left` \\| `right` \\| `center` | Flotaci\xF3n (sin `width`, `max-width: 50%`) |\n| `class` | texto | Clases CSS adicionales |\n| `style` | CSS | Estilos inline |\n\n## Interacci\xF3n\n\n- **Arrastra** el mango vertical para mover la l\xEDnea de corte.\n- **Haz clic** en cualquier punto para saltar el slider all\xED.\n- **Teclado**: `\u2190` y `\u2192` ajustan \xB15% (enfoca el comparador con Tab).'
2621
+ },
2622
+ {
2623
+ "id": "hover-3d",
2624
+ "category": "Componentes",
2625
+ "title": "Hover 3D",
2626
+ "icon": "view_in_ar",
2627
+ "order": 7,
2628
+ "md": "# Hover 3D\n\nLa directiva `:::hover-3d` convierte su contenido en una tarjeta con **efecto 3D** que sigue al rat\xF3n.\n\n## Sintaxis\n\n```md\n:::hover-3d\n![Monta\xF1as](https://img.daisyui.com/images/stock/photo-1506905925346-21bda4d32df4.webp){400:260}\n:::\n```\n\n:::hover-3d\n![Monta\xF1as](https://img.daisyui.com/images/stock/photo-1506905925346-21bda4d32df4.webp){400:260}\n:::\n\n## Con texto\n\n```md\n:::hover-3d\n## El efecto es autom\xE1tico\nMueve el rat\xF3n sobre la tarjeta: el contenido rota en 3D siguiendo el cursor.\n:::\n```\n\n:::hover-3d\n## El efecto es autom\xE1tico\nMueve el rat\xF3n sobre la tarjeta: el contenido rota en 3D siguiendo el cursor.\n:::\n\n## Props\n\n| Prop | Tipo | Descripci\xF3n |\n| --- | --- | --- |\n| `class` | texto | Clases CSS adicionales |\n| `style` | CSS | Estilos inline |\n\n## Notas\n\n- El contenedor genera 8 reflejos de luz (luces de borde) que reaccionan al movimiento.\n- Cuanto m\xE1s cerca del borde est\xE1 el cursor, m\xE1s rota la tarjeta.\n- En pantallas t\xE1ctiles el efecto se desactiva (no hay hover).\n- Se recomienda un solo hijo (imagen o bloque de texto) para el mejor resultado."
2629
+ },
2630
+ {
2631
+ "id": "hover-gallery",
2632
+ "category": "Componentes",
2633
+ "title": "Hover Gallery",
2634
+ "icon": "photo_library",
2635
+ "order": 8,
2636
+ "md": '# Hover Gallery\n\nLa directiva `:::hover-gallery` muestra una galer\xEDa donde las im\xE1genes se **expanden al pasar el rat\xF3n**, estilo dock.\n\n## Sintaxis\n\nLas im\xE1genes se ponen como markdown dentro del bloque:\n\n```md\n:::hover-gallery {aspect="16/9"}\n![1](https://img.daisyui.com/images/stock/photo-1506905925346-21bda4d32df4.webp)\n![2](https://img.daisyui.com/images/stock/photo-1470252649378-9c29740c9fa8.webp)\n![3](https://img.daisyui.com/images/stock/photo-1441974231531-c6227db76b6e.webp)\n![4](https://img.daisyui.com/images/stock/photo-1500530855697-b586d89ba3ee.webp)\n![5](https://img.daisyui.com/images/stock/photo-1534528741775-53994a69daeb.webp)\n![6](https://img.daisyui.com/images/stock/photo-1493863641943-9b68992a8d07.webp)\n:::\n```\n\n:::hover-gallery {aspect="16/9"}\n![1](https://img.daisyui.com/images/stock/photo-1506905925346-21bda4d32df4.webp)\n![2](https://img.daisyui.com/images/stock/photo-1470252649378-9c29740c9fa8.webp)\n![3](https://img.daisyui.com/images/stock/photo-1441974231531-c6227db76b6e.webp)\n![4](https://img.daisyui.com/images/stock/photo-1500530855697-b586d89ba3ee.webp)\n![5](https://img.daisyui.com/images/stock/photo-1534528741775-53994a69daeb.webp)\n![6](https://img.daisyui.com/images/stock/photo-1493863641943-9b68992a8d07.webp)\n:::\n\n## Props\n\n| Prop | Tipo | Descripci\xF3n |\n| --- | --- | --- |\n| `aspect` | ratio | Proporci\xF3n de la galer\xEDa (default `16/9`) |\n| `class` | texto | Clases CSS adicionales |\n| `style` | CSS | Estilos inline |\n\n## Notas\n\n- La imagen activa (hover) crece y las vecinas se apartan para darle espacio.\n- Las im\xE1genes se recortan (`object-fit: cover`) para mantener la altura uniforme.\n- Funciona con 3 o m\xE1s im\xE1genes; con menos, se reparten el ancho.'
2637
+ },
2638
+ {
2639
+ "id": "chat",
2640
+ "category": "Componentes",
2641
+ "title": "Chat",
2642
+ "icon": "chat_bubble",
2643
+ "order": 9,
2644
+ "md": '# Chat\n\nLa directiva `:::chat` muestra **burbujas de conversaci\xF3n** (`:::chat-item`) estilo app de mensajer\xEDa.\n\n## Sintaxis\n\n```md\n:::chat\n:::chat-item {side="start" name="Ana" time="10:04"}\nHola, \xBFterminaste la documentaci\xF3n?\n:::\n:::chat-item {side="end" name="T\xFA" time="10:05"}\n\xA1S\xED! La gu\xEDa renderiza hasta directivas dentro del chat.\n:::\n:::chat-item {side="start" name="Ana" time="10:06"}\nIncre\xEDble. El motor escribe solo.\n:::\n:::\n```\n\n:::chat\n:::chat-item {side="start" name="Ana" time="10:04"}\nHola, \xBFterminaste la documentaci\xF3n?\n:::\n:::chat-item {side="end" name="T\xFA" time="10:05"}\n\xA1S\xED! La gu\xEDa renderiza hasta directivas dentro del chat.\n:::\n:::chat-item {side="start" name="Ana" time="10:06"}\nIncre\xEDble. El motor escribe solo.\n:::\n:::\n\n## Con avatar y color\n\n```md\n:::chat\n:::chat-item {side="start" name="Soporte" time="11:00" avatar="https://img.daisyui.com/images/stock/photo-1534528741775-53994a69daeb.webp" color="info" footer="Atendido"}\n\xBFEn qu\xE9 podemos ayudarte?\n:::\n:::chat-item {side="end" name="T\xFA" time="11:02" color="secondary" footer="Enviado"}\n\xBFC\xF3mo a\xF1ado un avatar personalizado?\n:::\n:::\n```\n\n:::chat\n:::chat-item {side="start" name="Soporte" time="11:00" avatar="https://img.daisyui.com/images/stock/photo-1534528741775-53994a69daeb.webp" color="info" footer="Atendido"}\n\xBFEn qu\xE9 podemos ayudarte?\n:::\n:::chat-item {side="end" name="T\xFA" time="11:02" color="secondary" footer="Enviado"}\n\xBFC\xF3mo a\xF1ado un avatar personalizado?\n:::\n:::\n\n## Props\n\n### `:::chat-item`\n\n| Prop | Tipo | Descripci\xF3n |\n| --- | --- | --- |\n| `side` | `start` \\| `end` | Burbuja a la izquierda o derecha (default `start`) |\n| `name` | texto | Nombre del autor |\n| `time` | texto | Hora mostrada bajo el nombre |\n| `avatar` | URL | Imagen del avatar |\n| `color` | `neutral` \\| `primary` \\| `secondary` \\| `accent` \\| `info` \\| `success` \\| `warning` \\| `error` | Color de la burbuja |\n| `footer` | texto | Pie del mensaje |\n| `class` | texto | Clases CSS adicionales |\n| `style` | CSS | Estilos inline |\n\n## Notas\n\n- El contenido de cada `chat-item` admite **markdown completo** (c\xF3digo, tablas, enlaces...).\n- Puedes poner varios `:::chat` en el documento; cada uno es un grupo independiente.'
2645
+ },
2646
+ {
2647
+ "id": "richlist",
2648
+ "category": "Componentes",
2649
+ "title": "Richlist",
2650
+ "icon": "playlist_play",
2651
+ "order": 10,
2652
+ "md": '# Richlist\n\nLa directiva `:::richlist` muestra una **lista enriquecida** (`:::richlist-item`) con imagen, t\xEDtulos, subt\xEDtulo e iconos.\n\n## Sintaxis\n\n```md\n:::richlist\n:::richlist-item {title="Vim" subtitle="Editor de texto" image="https://img.daisyui.com/images/stock/photo-1493863641943-9b68992a8d07.webp"}\n:::richlist-item {title="Git" subtitle="Control de versiones" icon="code" icon2="terminal"}\n:::richlist-item {title="Docker" subtitle="Contenedores" icon="deployed_code"}\n:::\n```\n\n:::richlist\n:::richlist-item {title="Vim" subtitle="Editor de texto" image="https://img.daisyui.com/images/stock/photo-1493863641943-9b68992a8d07.webp"}\n:::richlist-item {title="Git" subtitle="Control de versiones" icon="code" icon2="terminal"}\n:::richlist-item {title="Docker" subtitle="Contenedores" icon="deployed_code"}\n:::\n\n## Con iconos en ambos lados\n\n```md\n:::richlist\n:::richlist-item {title="Modo oscuro" subtitle="Menos fatiga visual" icon="dark_mode" icon2="chevron_right"}\n:::richlist-item {title="Atajos" subtitle="M\xE1s velocidad" icon="keyboard" icon2="chevron_right"}\n:::richlist-item {title="Tema" subtitle="Personaliza colores" icon="palette" icon2="chevron_right"}\n:::\n```\n\n:::richlist\n:::richlist-item {title="Modo oscuro" subtitle="Menos fatiga visual" icon="dark_mode" icon2="chevron_right"}\n:::richlist-item {title="Atajos" subtitle="M\xE1s velocidad" icon="keyboard" icon2="chevron_right"}\n:::richlist-item {title="Tema" subtitle="Personaliza colores" icon="palette" icon2="chevron_right"}\n:::\n\n## Props\n\n### `:::richlist-item`\n\n| Prop | Tipo | Descripci\xF3n |\n| --- | --- | --- |\n| `title` | texto | T\xEDtulo del elemento |\n| `subtitle` | texto | Subt\xEDtulo (segunda l\xEDnea) |\n| `image` | URL | Imagen a la izquierda (sustituye a `icon`) |\n| `icon` | nombre Material | Icono a la izquierda |\n| `icon2` | nombre Material | Icono a la derecha |\n| `url` | URL | El bot\xF3n del `icon` abre el enlace en una pesta\xF1a nueva |\n| `url2` | URL | El bot\xF3n del `icon2` abre el enlace en una pesta\xF1a nueva |\n| `event` | `evento: fn` | Handler del bot\xF3n `icon` (funci\xF3n global, ver abajo) |\n| `event2` | `evento: fn` | Handler del bot\xF3n `icon2` (funci\xF3n global, ver abajo) |\n| `class` | texto | Clases CSS adicionales |\n| `style` | CSS | Estilos inline |\n\n### `:::richlist`\n\n| Prop | Tipo | Descripci\xF3n |\n| --- | --- | --- |\n| `class` | texto | Clases CSS adicionales |\n| `style` | CSS | Estilos inline |\n\n## Botones con acci\xF3n\n\nCada bot\xF3n de icono puede navegar (`url`/`url2`) o llamar a una funci\xF3n global (`event`/`event2`). `event` tiene prioridad sobre `url` en el mismo bot\xF3n:\n\n```md\n:::richlist\n:::richlist-item {title="Reproductor" subtitle="Demo de botones" icon="open_in_new" url="https://example.com" icon2="volume_up" event="click: reproducirSonido"}\nEl primer bot\xF3n abre una pesta\xF1a nueva; el segundo ejecuta una funci\xF3n global al hacer clic.\n:::\n:::\n```\n\n:::richlist\n:::richlist-item {title="Reproductor" subtitle="Demo de botones" icon="open_in_new" url="https://example.com" icon2="volume_up" event="click: reproducirSonido"}\nEl primer bot\xF3n abre una pesta\xF1a nueva; el segundo ejecuta una funci\xF3n global al hacer clic.\n:::\n:::\n\n### Sintaxis de `event`\n\n```md\nevent="click: miFuncion" <!-- un solo evento -->\nevent="click: fn1; mouseover: fn2" <!-- varios, separados por ; -->\nevent="onclick: miFuncion" <!-- el prefijo "on" es opcional -->\n```\n\nLa funci\xF3n se resuelve desde el **scope global** en el momento del evento y se invoca con el elemento como `this` y el evento como argumento:\n\n```js\nwindow.reproducirSonido = function (event) {\n console.log(\'Click en:\', this);\n};\n```\n\n:::note\nSi la funci\xF3n no existe, el bot\xF3n simplemente no hace nada (sin errores). El `event` es contenido propio del autor, con el mismo modelo de confianza que `:::raw` o `:::style`.\n:::'
2653
+ },
2654
+ {
2655
+ "id": "stat",
2656
+ "category": "Componentes",
2657
+ "title": "Stat",
2658
+ "icon": "insights",
2659
+ "order": 11,
2660
+ "md": '# Stat\n\nLa directiva `:::stat` muestra una **estad\xEDstica** con icono, valor y descripci\xF3n. Las estad\xEDsticas **consecutivas** se agrupan en una fila.\n\n## Sintaxis\n\n```md\n:::stat {title="Descargas" value="31K" icon="download" color="success"}\n:::stat {title="Nuevos usuarios" value="4,200" icon="group_add" color="primary"}\n:::stat {title="Retenci\xF3n" value="82%" icon="trending_up" color="info"}\n:::\n```\n\n:::stat {title="Descargas" value="31K" icon="download" color="success"}\n:::stat {title="Nuevos usuarios" value="4,200" icon="group_add" color="primary"}\n:::stat {title="Retenci\xF3n" value="82%" icon="trending_up" color="info"}\n:::\n\n## Con descripci\xF3n\n\n```md\n:::stat {title="Ingresos" value="$14,320" desc="+12% este mes" icon="payments" color="secondary"}\n:::stat {title="Errores" value="3" desc="resueltos hoy" icon="bug_report" color="warning"}\n:::\n```\n\n:::stat {title="Ingresos" value="$14,320" desc="+12% este mes" icon="payments" color="secondary"}\n:::stat {title="Errores" value="3" desc="resueltos hoy" icon="bug_report" color="warning"}\n:::\n\n## Prop individual (sin agrupar)\n\n```md\n:::stat {title="Tiempo de actividad" value="99.9%" icon="monitor_heart" color="success"}\n```\n\n:::stat {title="Tiempo de actividad" value="99.9%" icon="monitor_heart" color="success"}\n:::\n\n## Props\n\n| Prop | Tipo | Descripci\xF3n |\n| --- | --- | --- |\n| `title` | texto | Etiqueta superior |\n| `value` | texto | Valor principal (grande) |\n| `desc` | texto | Descripci\xF3n bajo el valor |\n| `icon` | nombre Material | Icono lateral |\n| `color` | `primary` \\| `secondary` \\| `info` \\| `success` \\| `warning` \\| `error` | Color del icono y valor (default `primary`) |\n| `class` | texto | Clases CSS adicionales |\n| `style` | CSS | Estilos inline |\n\n> Cada `:::stat` debe cerrarse con su `:::`. Las stats contiguas se agrupan en fila autom\xE1ticamente; para separarlas deja texto entre medias.'
2661
+ },
2662
+ {
2663
+ "id": "details",
2664
+ "category": "Interactivos",
2665
+ "title": "Details",
2666
+ "icon": "expand_more",
2667
+ "order": 1,
2668
+ "md": '# Details\n\nLa directiva `:::details` crea un bloque **plegable** nativo (`<details>`), \xFAtil para respuestas largas o contenido oculto.\n\n## Sintaxis\n\n```md\n:::details {title="\xBFQu\xE9 es NoirMD?"}\nEditor y motor de markdown con directivas propias.\n:::\n```\n\n:::details {title="\xBFQu\xE9 es NoirMD?"}\nEditor y motor de markdown con directivas propias.\n:::\n\n## Abierto por defecto\n\n```md\n:::details {title="Atajos del editor" defaultOpen="true"}\n| Atajo | Acci\xF3n |\n| --- | --- |\n| `Ctrl+S` | Guardar |\n| `Ctrl+K` | Alternar preview |\n| `Ctrl+F` | Buscar |\n:::\n```\n\n:::details {title="Atajos del editor" defaultOpen="true"}\n| Atajo | Acci\xF3n |\n| --- | --- |\n| `Ctrl+S` | Guardar |\n| `Ctrl+K` | Alternar preview |\n| `Ctrl+F` | Buscar |\n:::\n\n## Con icono\n\n```md\n:::details {title="Soluci\xF3n del ejercicio" icon="lightbulb"}\nEl c\xF3digo resultante:\n\n```js\nconsole.log(\'\xA1Resuelto!\');\n```\n:::\n```\n\n:::details {title="Soluci\xF3n del ejercicio" icon="lightbulb"}\nEl c\xF3digo resultante:\n\n```js\nconsole.log(\'\xA1Resuelto!\');\n```\n:::\n\n## Props\n\n| Prop | Tipo | Descripci\xF3n |\n| --- | --- | --- |\n| `title` | texto | T\xEDtulo del desplegable |\n| `icon` | nombre Material | Icono junto al t\xEDtulo |\n| `defaultOpen` | `true` | Abierto al cargar |\n| `class` | texto | Clases CSS adicionales |\n| `style` | CSS | Estilos inline |\n\n> El contenido admite markdown completo y directivas anidadas.'
2669
+ },
2670
+ {
2671
+ "id": "modal",
2672
+ "category": "Interactivos",
2673
+ "title": "Modal",
2674
+ "icon": "open_in_full",
2675
+ "order": 2,
2676
+ "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 (default: `title` o \xABOpen\xBB) |\n| `icon` | nombre Material | Icono del bot\xF3n (default `open_in_new`) |\n| `class` | texto | Clases CSS adicionales |\n| `style` | CSS | Estilos inline |\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.'
2677
+ },
2678
+ {
2679
+ "id": "button",
2680
+ "category": "Interactivos",
2681
+ "title": "Button",
2682
+ "icon": "touch_app",
2683
+ "order": 3,
2684
+ "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 `near_me`) |\n| `target` | `_blank` \\| `_self` \\| ... | Destino del enlace (default `_blank`) |\n| `class` | texto | Clases CSS adicionales |\n| `style` | CSS | Estilos inline |'
2685
+ },
2686
+ {
2687
+ "id": "slide",
2688
+ "category": "Interactivos",
2689
+ "title": "Slide",
2690
+ "icon": "slideshow",
2691
+ "order": 4,
2692
+ "md": '# Slide\n\nLa directiva `:::slide` convierte su contenido en un **slider autom\xE1tico** (diapositivas con fade).\n\n## Sintaxis\n\nLas secciones se separan con `---`:\n\n```md\n:::slide {interval="2500"}\n## Diapositiva 1\n\nBienvenido a la **gu\xEDa interactiva**.\n\n---\n\n## Diapositiva 2\n\nCada `---` separa una diapositiva nueva.\n\n---\n\n## Diapositiva 3\n\nY el motor se encarga del resto.\n:::\n```\n\n:::slide {interval="2500"}\n## Diapositiva 1\n\nBienvenido a la **gu\xEDa interactiva**.\n\n---\n\n## Diapositiva 2\n\nCada `---` separa una diapositiva nueva.\n\n---\n\n## Diapositiva 3\n\nY el motor se encarga del resto.\n:::\n\n## Con contenido variado\n\n```md\n:::slide {interval="3500" speed="800"}\n:::card {title="Card" icon="dashboard"}\nLas directivas se anidan dentro.\n:::\n---\n> **Admonici\xF3n** como diapositiva\n---\n| P\xE1gina | Tema |\n| --- | --- |\n| 1 | Slide |\n| 2 | Loop |\n:::\n```\n\n:::slide {interval="3500" speed="800"}\n:::card {title="Card" icon="dashboard"}\nLas directivas se anidan dentro.\n:::\n---\n> **Admonici\xF3n** como diapositiva\n---\n| P\xE1gina | Tema |\n| --- | --- |\n| 1 | Slide |\n| 2 | Loop |\n:::\n\n## Props\n\n| Prop | Tipo | Descripci\xF3n |\n| --- | --- | --- |\n| `interval` | ms | Tiempo por diapositiva (default `3000`) |\n| `speed` | ms | Duraci\xF3n de la transici\xF3n (default `500`) |\n| `class` | texto | Clases CSS adicionales |\n| `style` | CSS | Estilos inline |\n\n## Notas\n\n- Los puntos inferiores permiten saltar a una diapositiva.\n- Al llegar a la \xFAltima, vuelve a la primera autom\xE1ticamente (loop).\n- La barra de progreso superior muestra el avance del ciclo.'
2693
+ },
2694
+ {
2695
+ "id": "div",
2696
+ "category": "Layout",
2697
+ "title": "Div (contenedor)",
2698
+ "icon": "square_foot",
2699
+ "order": 1,
2700
+ "md": '# Div (contenedor)\n\nLa directiva `:::div` envuelve su contenido en un `<div>` con **clases, id o estilos** propios.\n\n## Sintaxis\n\n```md\n:::div {.mi-clase #mi-id}\nContenido dentro del div.\n:::\n```\n\n:::div {.mi-clase #mi-id}\nContenido dentro del div.\n:::\n\n## Aplicando clases\n\n```md\n:::div {.test-container}\nTarjeta con estilo personalizado.\n:::\n```\n\n:::div {.test-container}\nTarjeta con estilo personalizado.\n:::\n\n## Con estilos inline\n\n```md\n:::div {style="border: 1px dashed var(--color-accent-primary, #0ea5e9); padding: 1rem; border-radius: 10px;"}\nCaja con borde discontinuo y padding.\n:::\n```\n\n:::div {style="border: 1px dashed var(--color-accent-primary, #0ea5e9); padding: 1rem; border-radius: 10px;"}\nCaja con borde discontinuo y padding.\n:::\n\n## Contenido enriquecido\n\n```md\n:::div {.test-container}\n## T\xEDtulo dentro del div\n\n:::note\nLas directivas funcionan anidadas dentro del div.\n:::\n:::\n```\n\n:::div {.test-container}\n## T\xEDtulo dentro del div\n\n:::note\nLas directivas funcionan anidadas dentro del div.\n:::\n:::\n\n## Props\n\n| Prop | Tipo | Descripci\xF3n |\n| --- | --- | --- |\n| `.clase` | texto | Clases CSS (prefijo `.`, varias separadas por espacio) |\n| `#id` | texto | Id del contenedor (prefijo `#`) |\n| `style` | CSS | Estilos inline |\n\n## Usos t\xEDpicos\n\n- Agrupar varios componentes para darles un fondo o borde com\xFAn.\n- Contenedor centrado: `:::div {style="max-width: 600px; margin: 0 auto;"}`.\n- Combinar con `:::style` para CSS reutilizable por clase.'
2701
+ },
2702
+ {
2703
+ "id": "style",
2704
+ "category": "Layout",
2705
+ "title": "Style (CSS)",
2706
+ "icon": "palette",
2707
+ "order": 2,
2708
+ "md": "# Style (CSS)\n\nLa directiva `:::style` inyecta **CSS global** al documento renderizado.\n\n## Sintaxis\n\n```md\n:::style\n.mi-clase {\n background: #f1f5f9;\n border-radius: 10px;\n padding: 1rem;\n}\n:::\n```\n\n## Ejemplo combinado con div\n\n```md\n:::style\n.box-demo {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 1rem;\n padding: 1rem;\n border-radius: 12px;\n background: color-mix(in srgb, var(--color-accent-primary, #0ea5e9) 10%, transparent);\n}\n.box-demo > div {\n padding: 1rem;\n border-radius: 8px;\n background: var(--color-background-secondary-solid, #1e293b);\n}\n:::\n\n:::div {.box-demo}\n**A**\n\n---\n**B**\n:::\n```\n\n:::style\n.box-demo {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 1rem;\n padding: 1rem;\n border-radius: 12px;\n background: color-mix(in srgb, var(--color-accent-primary, #0ea5e9) 10%, transparent);\n}\n.box-demo > div {\n padding: 1rem;\n border-radius: 8px;\n background: var(--color-background-secondary-solid, #1e293b);\n}\n:::\n\n:::div {.box-demo}\n**A**\n\n---\n**B**\n:::\n\n## Props\n\n| Prop | Tipo | Descripci\xF3n |\n| --- | --- | --- |\n| `class` | texto | Clases CSS adicionales |\n| `style` | CSS | Estilos inline |\n\n## Notas\n\n- El CSS se aplica al **documento renderizado completo**, no solo al bloque.\n- Define clases una vez al principio y \xFAsalas despu\xE9s con `:::div` o props `class`.\n- Dispones de las variables de tema del editor: `--color-background-primary`, `--color-text-primary`, `--color-accent-primary`, `--color-border`, etc."
2709
+ },
2710
+ {
2711
+ "id": "raw",
2712
+ "category": "Layout",
2713
+ "title": "Raw (HTML)",
2714
+ "icon": "code_off",
2715
+ "order": 3,
2716
+ "md": '# Raw (HTML)\n\nLa directiva `:::raw` (o su alias `:::custom`) inserta **HTML puro** sin procesar en el documento.\n\n## Sintaxis\n\n```md\n:::raw\n<div style="text-align: center; padding: 1rem; border: 1px solid #334155; border-radius: 10px;">\n HTML escrito a mano funciona tal cual.\n</div>\n:::\n```\n\n:::raw\n<div style="text-align: center; padding: 1rem; border: 1px solid #334155; border-radius: 10px;">\n HTML escrito a mano funciona tal cual.\n</div>\n:::\n\n## Elementos interactivos\n\n```md\n:::raw\n<details class="nr-details">\n <summary>Detalle nativo con <b>HTML</b></summary>\n <p>Los atributos, estilos y eventos se conservan intactos.</p>\n</details>\n:::\n```\n\n:::raw\n<details class="nr-details">\n <summary>Detalle nativo con <b>HTML</b></summary>\n <p>Los atributos, estilos y eventos se conservan intactos.</p>\n</details>\n:::\n\n## Props\n\n| Prop | Tipo | Descripci\xF3n |\n| --- | --- | --- |\n| `class` | texto | Clases CSS adicionales |\n| `style` | CSS | Estilos inline |\n\n## Cu\xE1ndo usar `raw`\n\n- Insertar embeds (`iframe`, `video`, widgets).\n- Marcar up estructuras que el markdown no cubre.\n- Prototipar HTML antes de convertirlo a directiva.\n\n> \u26A0\uFE0F Al ser HTML sin filtrar, \xFAsalo solo con contenido de confianza.'
2717
+ }
2718
+ ];
2719
+
2720
+ // react/Guide.tsx
1898
2721
  var import_jsx_runtime2 = require("react/jsx-runtime");
2722
+ var Guide = ({
2723
+ open,
2724
+ onClose,
2725
+ initialDirective,
2726
+ search = true
2727
+ }) => {
2728
+ const [query, setQuery] = (0, import_react4.useState)("");
2729
+ const [selectedId, setSelectedId] = (0, import_react4.useState)(null);
2730
+ const [collapsed, setCollapsed] = (0, import_react4.useState)({});
2731
+ const contentRef = (0, import_react4.useRef)(null);
2732
+ const groups = (0, import_react4.useMemo)(() => {
2733
+ const map = /* @__PURE__ */ new Map();
2734
+ for (const e of guideData) {
2735
+ const arr = map.get(e.category) ?? [];
2736
+ arr.push(e);
2737
+ map.set(e.category, arr);
2738
+ }
2739
+ return [...map.entries()];
2740
+ }, []);
2741
+ const filtered = (0, import_react4.useMemo)(() => {
2742
+ const q = query.trim().toLowerCase();
2743
+ if (!q) return groups;
2744
+ return groups.map(([cat, entries]) => [
2745
+ cat,
2746
+ entries.filter(
2747
+ (e) => e.title.toLowerCase().includes(q) || e.id.toLowerCase().includes(q) || e.category.toLowerCase().includes(q) || e.md.slice(0, 400).toLowerCase().includes(q)
2748
+ )
2749
+ ]).filter(([, entries]) => entries.length > 0);
2750
+ }, [groups, query]);
2751
+ const selected = (0, import_react4.useMemo)(
2752
+ () => guideData.find((e) => e.id === selectedId) ?? null,
2753
+ [selectedId]
2754
+ );
2755
+ (0, import_react4.useEffect)(() => {
2756
+ if (!open) return;
2757
+ setQuery("");
2758
+ if (!selectedId) {
2759
+ const initial = guideData.find((e) => e.id === initialDirective) ?? guideData.find((e) => e.id === "introduccion") ?? guideData[0];
2760
+ setSelectedId(initial?.id ?? null);
2761
+ }
2762
+ }, [open]);
2763
+ (0, import_react4.useEffect)(() => {
2764
+ if (!open) return;
2765
+ const prev = document.body.style.overflow;
2766
+ document.body.style.overflow = "hidden";
2767
+ return () => {
2768
+ document.body.style.overflow = prev;
2769
+ };
2770
+ }, [open]);
2771
+ (0, import_react4.useEffect)(() => {
2772
+ if (!open) return;
2773
+ contentRef.current?.scrollTo({ top: 0 });
2774
+ }, [selectedId, open]);
2775
+ (0, import_react4.useEffect)(() => {
2776
+ if (!open) return;
2777
+ const onKey = (e) => {
2778
+ if (e.key === "Escape") onClose();
2779
+ };
2780
+ window.addEventListener("keydown", onKey);
2781
+ return () => window.removeEventListener("keydown", onKey);
2782
+ }, [open, onClose]);
2783
+ if (!open) return null;
2784
+ const toggleCategory = (cat) => setCollapsed((prev) => ({ ...prev, [cat]: !prev[cat] }));
2785
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "nr-guide", role: "dialog", "aria-modal": "true", "aria-label": "Gu\xEDa de sintaxis", children: [
2786
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "nr-guide__overlay", onClick: onClose }),
2787
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "nr-guide__panel", children: [
2788
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("header", { className: "nr-guide__head", children: [
2789
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "material-icons-round", children: "menu_book" }),
2790
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h2", { children: "Gu\xEDa de sintaxis" }),
2791
+ search && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2792
+ "input",
2793
+ {
2794
+ className: "nr-guide__search",
2795
+ type: "search",
2796
+ placeholder: "Buscar directiva\u2026",
2797
+ value: query,
2798
+ onChange: (e) => setQuery(e.target.value)
2799
+ }
2800
+ ),
2801
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2802
+ "button",
2803
+ {
2804
+ className: "nr-guide__close",
2805
+ onClick: onClose,
2806
+ "aria-label": "Cerrar gu\xEDa",
2807
+ children: "\xD7"
2808
+ }
2809
+ )
2810
+ ] }),
2811
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "nr-guide__body", children: [
2812
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("nav", { className: "nr-guide__nav", children: [
2813
+ filtered.map(([cat, entries]) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "nr-guide__cat", children: [
2814
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
2815
+ "button",
2816
+ {
2817
+ className: "nr-guide__cat-head",
2818
+ onClick: () => toggleCategory(cat),
2819
+ children: [
2820
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "material-icons-round nr-guide__cat-chevron", children: collapsed[cat] ? "chevron_right" : "expand_more" }),
2821
+ cat
2822
+ ]
2823
+ }
2824
+ ),
2825
+ !collapsed[cat] && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ul", { className: "nr-guide__items", children: entries.map((e) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
2826
+ "button",
2827
+ {
2828
+ className: `nr-guide__item${selectedId === e.id ? " nr-guide__item--active" : ""}`,
2829
+ onClick: () => setSelectedId(e.id),
2830
+ children: [
2831
+ e.icon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "material-icons-round nr-guide__item-icon", children: e.icon }),
2832
+ e.title
2833
+ ]
2834
+ }
2835
+ ) }, e.id)) })
2836
+ ] }, cat)),
2837
+ filtered.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "nr-guide__empty", children: [
2838
+ "Sin resultados para \xAB",
2839
+ query,
2840
+ "\xBB."
2841
+ ] })
2842
+ ] }),
2843
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "nr-guide__content", ref: contentRef, children: selected ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CustomMarkdownRenderer_default, { content: selected.md }, selected.id) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "nr-guide__empty", children: "Selecciona una directiva de la lista." }) })
2844
+ ] })
2845
+ ] })
2846
+ ] });
2847
+ };
2848
+ var Guide_default = Guide;
2849
+
2850
+ // react/NReditor.tsx
2851
+ var import_jsx_runtime3 = require("react/jsx-runtime");
1899
2852
  var customSyntaxHighlighting = import_language2.HighlightStyle.define([
1900
2853
  { tag: import_highlight2.tags.heading, fontWeight: "bold", color: "var(--tc-heading, #e2e8f0)" },
1901
2854
  { tag: import_highlight2.tags.quote, color: "var(--tc-quote, #94a3b8)", fontStyle: "italic" },
@@ -2346,13 +3299,33 @@ var NReditor = ({
2346
3299
  debounceMs = 300,
2347
3300
  tailwindCDN = false,
2348
3301
  onGuide,
2349
- onConfig
3302
+ onConfig,
3303
+ guide = false
2350
3304
  }) => {
2351
- const editorRef = (0, import_react4.useRef)(null);
2352
- const [isAllFolded, setIsAllFolded] = (0, import_react4.useState)(false);
2353
- const [editorMode, setEditorMode] = (0, import_react4.useState)("split");
3305
+ const editorRef = (0, import_react5.useRef)(null);
3306
+ const [isAllFolded, setIsAllFolded] = (0, import_react5.useState)(false);
3307
+ const [editorMode, setEditorMode] = (0, import_react5.useState)("split");
3308
+ const [guideOpen, setGuideOpen] = (0, import_react5.useState)(false);
2354
3309
  const debouncedContent = useDebounce(value, debounceMs);
2355
3310
  useLazyTailwindCDN(tailwindCDN);
3311
+ (0, import_react5.useEffect)(() => {
3312
+ const mq = window.matchMedia("(max-width: 639px)");
3313
+ const apply = () => {
3314
+ if (mq.matches && editorMode === "split") setEditorMode("editor");
3315
+ };
3316
+ apply();
3317
+ mq.addEventListener("change", apply);
3318
+ return () => mq.removeEventListener("change", apply);
3319
+ }, [editorMode]);
3320
+ (0, import_react5.useEffect)(() => {
3321
+ const mq = window.matchMedia("(max-width: 639px)");
3322
+ const handle = () => {
3323
+ if (mq.matches) setEditorMode((m) => m === "split" ? "editor" : m);
3324
+ };
3325
+ handle();
3326
+ mq.addEventListener("change", handle);
3327
+ return () => mq.removeEventListener("change", handle);
3328
+ }, []);
2356
3329
  const handleToggleFold = () => {
2357
3330
  if (!editorRef.current) return;
2358
3331
  if (isAllFolded) {
@@ -2363,7 +3336,7 @@ var NReditor = ({
2363
3336
  setIsAllFolded(true);
2364
3337
  }
2365
3338
  };
2366
- const extensions = import_react4.default.useMemo(
3339
+ const extensions = import_react5.default.useMemo(
2367
3340
  () => [
2368
3341
  customStreamParserV2,
2369
3342
  (0, import_view.lineNumbers)(),
@@ -2399,94 +3372,101 @@ var NReditor = ({
2399
3372
  { key: "split", icon: "vertical_split", label: "Split" },
2400
3373
  { key: "preview", icon: "visibility", label: "Preview" }
2401
3374
  ];
2402
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: `nr-editor ${className || ""}`, children: [
2403
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "nr-editor-toolbar", children: [
2404
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "nr-editor-toolbar-left", children: [
2405
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2406
- "button",
2407
- {
2408
- onClick: handleToggleFold,
2409
- className: "nr-toolbar-btn nr-toolbar-btn-fold",
2410
- title: isAllFolded ? "Expandir todo" : "Colapsar todo",
2411
- "aria-label": isAllFolded ? "Expandir todo" : "Colapsar todo",
2412
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "material-icons-round", children: isAllFolded ? "unfold_more" : "unfold_less" })
2413
- }
2414
- ),
2415
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "nr-toolbar-divider" }),
2416
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "nr-toolbar-mode-group", children: modeButtons.map((btn) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
2417
- "button",
2418
- {
2419
- onClick: () => setEditorMode(btn.key),
2420
- className: `nr-toolbar-btn nr-toolbar-btn-mode ${editorMode === btn.key ? "nr-toolbar-btn-mode--active" : ""} ${btn.key === "split" ? "nr-toolbar-btn-split" : ""}`,
2421
- title: btn.label,
2422
- children: [
2423
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "material-icons-round", children: btn.icon }),
2424
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "nr-toolbar-label", children: btn.label })
2425
- ]
2426
- },
2427
- btn.key
2428
- )) })
2429
- ] }),
2430
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "nr-editor-toolbar-right", children: [
2431
- onGuide && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
2432
- "button",
2433
- {
2434
- onClick: onGuide,
2435
- className: "nr-toolbar-btn nr-toolbar-btn-sm nr-toolbar-btn-guide",
2436
- title: "Gu\xEDa de sintaxis",
2437
- children: [
2438
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "material-icons-round", children: "menu_book" }),
2439
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "nr-toolbar-label", children: "Gu\xEDa" })
2440
- ]
2441
- }
2442
- ),
2443
- onGuide && onConfig && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "nr-toolbar-divider" }),
2444
- onConfig && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
2445
- "button",
2446
- {
2447
- onClick: onConfig,
2448
- className: "nr-toolbar-btn nr-toolbar-btn-sm nr-toolbar-btn-config",
2449
- title: "Configurar tema y metadata",
2450
- children: [
2451
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "material-icons-round", children: "tune" }),
2452
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "nr-toolbar-label", children: "Configurar" })
2453
- ]
2454
- }
2455
- )
2456
- ] })
2457
- ] }),
2458
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "nr-editor-body", children: [
2459
- (editorMode === "editor" || editorMode === "split") && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2460
- "div",
2461
- {
2462
- className: `nr-editor-pane ${editorMode === "split" ? "nr-editor-pane--half" : ""}`,
2463
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2464
- import_react_codemirror.default,
2465
- {
2466
- value,
2467
- onChange,
2468
- height: "auto",
2469
- onCreateEditor: (view) => {
2470
- editorRef.current = view;
2471
- },
2472
- basicSetup: {
2473
- lineNumbers: false,
2474
- foldGutter: false
3375
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
3376
+ "div",
3377
+ {
3378
+ className: `nr-editor ${editorMode === "split" ? "nr-editor--split" : ""} ${className || ""}`,
3379
+ children: [
3380
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "nr-editor-toolbar", children: [
3381
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "nr-editor-toolbar-left", children: [
3382
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3383
+ "button",
3384
+ {
3385
+ onClick: handleToggleFold,
3386
+ className: "nr-toolbar-btn nr-toolbar-btn-fold",
3387
+ title: isAllFolded ? "Expandir todo" : "Colapsar todo",
3388
+ "aria-label": isAllFolded ? "Expandir todo" : "Colapsar todo",
3389
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "material-icons-round", children: isAllFolded ? "unfold_more" : "unfold_less" })
3390
+ }
3391
+ ),
3392
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "nr-toolbar-divider" }),
3393
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "nr-toolbar-mode-group", children: modeButtons.map((btn) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
3394
+ "button",
3395
+ {
3396
+ onClick: () => setEditorMode(btn.key),
3397
+ className: `nr-toolbar-btn nr-toolbar-btn-mode ${editorMode === btn.key ? "nr-toolbar-btn-mode--active" : ""} ${btn.key === "split" ? "nr-toolbar-btn-split" : ""}`,
3398
+ title: btn.label,
3399
+ children: [
3400
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "material-icons-round", children: btn.icon }),
3401
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "nr-toolbar-label", children: btn.label })
3402
+ ]
2475
3403
  },
2476
- extensions
3404
+ btn.key
3405
+ )) })
3406
+ ] }),
3407
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "nr-editor-toolbar-right", children: [
3408
+ (guide || onGuide) && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
3409
+ "button",
3410
+ {
3411
+ onClick: () => guide ? setGuideOpen(true) : onGuide?.(),
3412
+ className: "nr-toolbar-btn nr-toolbar-btn-sm nr-toolbar-btn-guide",
3413
+ title: "Gu\xEDa de sintaxis",
3414
+ children: [
3415
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "material-icons-round", children: "menu_book" }),
3416
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "nr-toolbar-label", children: "Gu\xEDa" })
3417
+ ]
3418
+ }
3419
+ ),
3420
+ (guide || onGuide) && onConfig && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "nr-toolbar-divider" }),
3421
+ onConfig && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
3422
+ "button",
3423
+ {
3424
+ onClick: onConfig,
3425
+ className: "nr-toolbar-btn nr-toolbar-btn-sm nr-toolbar-btn-config",
3426
+ title: "Configurar tema y metadata",
3427
+ children: [
3428
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "material-icons-round", children: "tune" }),
3429
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "nr-toolbar-label", children: "Configurar" })
3430
+ ]
3431
+ }
3432
+ )
3433
+ ] })
3434
+ ] }),
3435
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "nr-editor-body", children: [
3436
+ (editorMode === "editor" || editorMode === "split") && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3437
+ "div",
3438
+ {
3439
+ className: `nr-editor-pane ${editorMode === "split" ? "nr-editor-pane--half" : ""}`,
3440
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3441
+ import_react_codemirror.default,
3442
+ {
3443
+ value,
3444
+ onChange,
3445
+ height: "auto",
3446
+ onCreateEditor: (view) => {
3447
+ editorRef.current = view;
3448
+ },
3449
+ basicSetup: {
3450
+ lineNumbers: false,
3451
+ foldGutter: false
3452
+ },
3453
+ extensions
3454
+ }
3455
+ )
3456
+ }
3457
+ ),
3458
+ (editorMode === "preview" || editorMode === "split") && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3459
+ "div",
3460
+ {
3461
+ className: `nr-editor-preview ${editorMode === "split" ? "nr-editor-preview--half" : ""}`,
3462
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "nr-editor-prose", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "nr-prose", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CustomMarkdownRenderer_default, { content: debouncedContent }) }) })
2477
3463
  }
2478
3464
  )
2479
- }
2480
- ),
2481
- (editorMode === "preview" || editorMode === "split") && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2482
- "div",
2483
- {
2484
- className: `nr-editor-preview ${editorMode === "split" ? "nr-editor-preview--half" : ""}`,
2485
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "nr-editor-prose", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "nr-prose", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CustomMarkdownRenderer_default, { content: debouncedContent }) }) })
2486
- }
2487
- )
2488
- ] })
2489
- ] });
3465
+ ] }),
3466
+ guide && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Guide_default, { open: guideOpen, onClose: () => setGuideOpen(false) })
3467
+ ]
3468
+ }
3469
+ );
2490
3470
  };
2491
3471
  var NReditor_default = NReditor;
2492
3472
  //# sourceMappingURL=NReditor.cjs.map