@particle-academy/fancy-code 0.9.1 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1635,105 +1635,6 @@ var CodeEditor = Object.assign(CodeEditorRoot, {
1635
1635
  Panel: CodeEditorPanel,
1636
1636
  StatusBar: CodeEditorStatusBar
1637
1637
  });
1638
-
1639
- // src/engine/markdown-render.ts
1640
- function escapeHtml(s) {
1641
- return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1642
- }
1643
- function escapeAttr(s) {
1644
- return s.replace(/"/g, "&quot;").replace(/'/g, "&#39;");
1645
- }
1646
- function inline(text) {
1647
- return text.replace(/!\[([^\]]*)\]\(([^)\s]+)(?:\s+"([^"]*)")?\)/g, (_m, alt, src, title) => `<img src="${escapeAttr(src)}" alt="${escapeAttr(alt)}"${title ? ` title="${escapeAttr(title)}"` : ""}>`).replace(/\[([^\]]+)\]\(([^)\s]+)(?:\s+"([^"]*)")?\)/g, (_m, t, href, title) => `<a href="${escapeAttr(href)}"${title ? ` title="${escapeAttr(title)}"` : ""} rel="noopener">${t}</a>`).replace(/`([^`]+)`/g, (_m, c) => `<code>${c}</code>`).replace(/(\*\*|__)(?=\S)([\s\S]*?\S)\1/g, "<strong>$2</strong>").replace(/(\*|_)(?=\S)([\s\S]*?\S)\1/g, "<em>$2</em>");
1648
- }
1649
- function renderMarkdown(markdown) {
1650
- const lines = markdown.replace(/\r\n?/g, "\n").split("\n");
1651
- const out = [];
1652
- let i = 0;
1653
- let para = [];
1654
- let listType = null;
1655
- const flushPara = () => {
1656
- if (para.length) {
1657
- out.push(`<p>${inline(para.join(" "))}</p>`);
1658
- para = [];
1659
- }
1660
- };
1661
- const closeList = () => {
1662
- if (listType) {
1663
- out.push(`</${listType}>`);
1664
- listType = null;
1665
- }
1666
- };
1667
- while (i < lines.length) {
1668
- const line = lines[i];
1669
- const fence = line.match(/^\s*(```|~~~)(.*)$/);
1670
- if (fence) {
1671
- flushPara();
1672
- closeList();
1673
- const lang = fence[2].trim();
1674
- const body = [];
1675
- i++;
1676
- while (i < lines.length && !/^\s*(```|~~~)/.test(lines[i])) {
1677
- body.push(lines[i]);
1678
- i++;
1679
- }
1680
- i++;
1681
- out.push(`<pre><code${lang ? ` class="language-${escapeAttr(escapeHtml(lang))}"` : ""}>${escapeHtml(body.join("\n"))}</code></pre>`);
1682
- continue;
1683
- }
1684
- const esc = escapeHtml(line);
1685
- if (/^\s*$/.test(line)) {
1686
- flushPara();
1687
- closeList();
1688
- i++;
1689
- continue;
1690
- }
1691
- const heading = esc.match(/^(#{1,6})\s+(.*)$/);
1692
- if (heading) {
1693
- flushPara();
1694
- closeList();
1695
- const level = heading[1].length;
1696
- out.push(`<h${level}>${inline(heading[2])}</h${level}>`);
1697
- i++;
1698
- continue;
1699
- }
1700
- if (/^\s*([-*_])(\s*\1){2,}\s*$/.test(line)) {
1701
- flushPara();
1702
- closeList();
1703
- out.push("<hr>");
1704
- i++;
1705
- continue;
1706
- }
1707
- const quote = esc.match(/^\s*&gt;\s?(.*)$/);
1708
- if (quote) {
1709
- flushPara();
1710
- closeList();
1711
- out.push(`<blockquote>${inline(quote[1])}</blockquote>`);
1712
- i++;
1713
- continue;
1714
- }
1715
- const ol = esc.match(/^\s*\d+\.\s+(.*)$/);
1716
- const ul = esc.match(/^\s*[-*+]\s+(.*)$/);
1717
- if (ol || ul) {
1718
- flushPara();
1719
- const type = ol ? "ol" : "ul";
1720
- if (listType !== type) {
1721
- closeList();
1722
- out.push(`<${type}>`);
1723
- listType = type;
1724
- }
1725
- out.push(`<li>${inline((ol ?? ul)[1])}</li>`);
1726
- i++;
1727
- continue;
1728
- }
1729
- closeList();
1730
- para.push(esc);
1731
- i++;
1732
- }
1733
- flushPara();
1734
- closeList();
1735
- return out.join("\n");
1736
- }
1737
1638
  function MarkdownEditor({
1738
1639
  value,
1739
1640
  defaultValue = "",
@@ -1747,7 +1648,7 @@ function MarkdownEditor({
1747
1648
  minHeight,
1748
1649
  maxHeight,
1749
1650
  className,
1750
- renderPreview = renderMarkdown
1651
+ renderPreview
1751
1652
  }) {
1752
1653
  const [content, setContent] = reactFancy.useControllableState(value, defaultValue, onValueChange);
1753
1654
  const showEditor = mode !== "preview";
@@ -1780,7 +1681,19 @@ function MarkdownEditor({
1780
1681
  {
1781
1682
  "data-fancy-markdown-preview": "",
1782
1683
  className: "fancy-md-preview",
1783
- dangerouslySetInnerHTML: { __html: renderPreview(content) }
1684
+ style: maxHeight ? { maxHeight, overflowY: "auto" } : void 0,
1685
+ children: renderPreview ? (
1686
+ // The host imposed its own pipeline; its HTML is injected as-is and
1687
+ // is its own responsibility to sanitise.
1688
+ /* @__PURE__ */ jsxRuntime.jsx("div", { dangerouslySetInnerHTML: { __html: renderPreview(content) } })
1689
+ ) : (
1690
+ // The kit's own renderer — a full CommonMark + GFM parse that
1691
+ // sanitises by default. `marked` already sits in this tree as a
1692
+ // react-fancy runtime dependency, so the built-in renderer this
1693
+ // replaced was a second, weaker markdown implementation next to a
1694
+ // complete one.
1695
+ /* @__PURE__ */ jsxRuntime.jsx(reactFancy.ContentRenderer, { value: content, format: "markdown" })
1696
+ )
1784
1697
  }
1785
1698
  )
1786
1699
  ]
@@ -1862,6 +1775,105 @@ function FileViewer({
1862
1775
  );
1863
1776
  }
1864
1777
  FileViewer.displayName = "FileViewer";
1778
+
1779
+ // src/engine/markdown-render.ts
1780
+ function escapeHtml(s) {
1781
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1782
+ }
1783
+ function escapeAttr(s) {
1784
+ return s.replace(/"/g, "&quot;").replace(/'/g, "&#39;");
1785
+ }
1786
+ function inline(text) {
1787
+ return text.replace(/!\[([^\]]*)\]\(([^)\s]+)(?:\s+"([^"]*)")?\)/g, (_m, alt, src, title) => `<img src="${escapeAttr(src)}" alt="${escapeAttr(alt)}"${title ? ` title="${escapeAttr(title)}"` : ""}>`).replace(/\[([^\]]+)\]\(([^)\s]+)(?:\s+"([^"]*)")?\)/g, (_m, t, href, title) => `<a href="${escapeAttr(href)}"${title ? ` title="${escapeAttr(title)}"` : ""} rel="noopener">${t}</a>`).replace(/`([^`]+)`/g, (_m, c) => `<code>${c}</code>`).replace(/(\*\*|__)(?=\S)([\s\S]*?\S)\1/g, "<strong>$2</strong>").replace(/(\*|_)(?=\S)([\s\S]*?\S)\1/g, "<em>$2</em>");
1788
+ }
1789
+ function renderMarkdown(markdown) {
1790
+ const lines = markdown.replace(/\r\n?/g, "\n").split("\n");
1791
+ const out = [];
1792
+ let i = 0;
1793
+ let para = [];
1794
+ let listType = null;
1795
+ const flushPara = () => {
1796
+ if (para.length) {
1797
+ out.push(`<p>${inline(para.join(" "))}</p>`);
1798
+ para = [];
1799
+ }
1800
+ };
1801
+ const closeList = () => {
1802
+ if (listType) {
1803
+ out.push(`</${listType}>`);
1804
+ listType = null;
1805
+ }
1806
+ };
1807
+ while (i < lines.length) {
1808
+ const line = lines[i];
1809
+ const fence = line.match(/^\s*(```|~~~)(.*)$/);
1810
+ if (fence) {
1811
+ flushPara();
1812
+ closeList();
1813
+ const lang = fence[2].trim();
1814
+ const body = [];
1815
+ i++;
1816
+ while (i < lines.length && !/^\s*(```|~~~)/.test(lines[i])) {
1817
+ body.push(lines[i]);
1818
+ i++;
1819
+ }
1820
+ i++;
1821
+ out.push(`<pre><code${lang ? ` class="language-${escapeAttr(escapeHtml(lang))}"` : ""}>${escapeHtml(body.join("\n"))}</code></pre>`);
1822
+ continue;
1823
+ }
1824
+ const esc = escapeHtml(line);
1825
+ if (/^\s*$/.test(line)) {
1826
+ flushPara();
1827
+ closeList();
1828
+ i++;
1829
+ continue;
1830
+ }
1831
+ const heading = esc.match(/^(#{1,6})\s+(.*)$/);
1832
+ if (heading) {
1833
+ flushPara();
1834
+ closeList();
1835
+ const level = heading[1].length;
1836
+ out.push(`<h${level}>${inline(heading[2])}</h${level}>`);
1837
+ i++;
1838
+ continue;
1839
+ }
1840
+ if (/^\s*([-*_])(\s*\1){2,}\s*$/.test(line)) {
1841
+ flushPara();
1842
+ closeList();
1843
+ out.push("<hr>");
1844
+ i++;
1845
+ continue;
1846
+ }
1847
+ const quote = esc.match(/^\s*&gt;\s?(.*)$/);
1848
+ if (quote) {
1849
+ flushPara();
1850
+ closeList();
1851
+ out.push(`<blockquote>${inline(quote[1])}</blockquote>`);
1852
+ i++;
1853
+ continue;
1854
+ }
1855
+ const ol = esc.match(/^\s*\d+\.\s+(.*)$/);
1856
+ const ul = esc.match(/^\s*[-*+]\s+(.*)$/);
1857
+ if (ol || ul) {
1858
+ flushPara();
1859
+ const type = ol ? "ol" : "ul";
1860
+ if (listType !== type) {
1861
+ closeList();
1862
+ out.push(`<${type}>`);
1863
+ listType = type;
1864
+ }
1865
+ out.push(`<li>${inline((ol ?? ul)[1])}</li>`);
1866
+ i++;
1867
+ continue;
1868
+ }
1869
+ closeList();
1870
+ para.push(esc);
1871
+ i++;
1872
+ }
1873
+ flushPara();
1874
+ closeList();
1875
+ return out.join("\n");
1876
+ }
1865
1877
  function useCodeEditorInputTagAdapter() {
1866
1878
  const ctx = useCodeEditor();
1867
1879
  return react.useMemo(() => reactFancy.textareaAdapter(ctx.textareaRef), [ctx.textareaRef]);