markdownr 0.5.11 → 0.5.12
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.
- checksums.yaml +4 -4
- data/lib/markdown_server/app.rb +6 -0
- data/lib/markdown_server/version.rb +1 -1
- data/views/layout.erb +151 -6
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 280f1aab156292d75fc2e561f222bb7b92870cb85a68df13494f4614855f437f
|
|
4
|
+
data.tar.gz: dd83fcbf5303eb687017a7bf5783d4557a5e723e367c5c0641c0e072e8ddab23
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b31d87695427970ae2dd48e4f01b3b39ca5e7e1b7ff1a57a83d231d386881975158a72d4501bb57d838f8247f832892483b70a815fba7b46ae8099eabce3da2d
|
|
7
|
+
data.tar.gz: 7e46d20ad61fd47243ad69b02ed95e282817e94b5726d5c3f8d46b1b72a9af4856de17de71b234d0fb2b8a6e506cb21bd4e199c0f1460e38e3c02463cb33a704
|
data/lib/markdown_server/app.rb
CHANGED
|
@@ -125,6 +125,12 @@ module MarkdownServer
|
|
|
125
125
|
end
|
|
126
126
|
|
|
127
127
|
def render_markdown(text)
|
|
128
|
+
# Convert mermaid code fences to raw HTML divs before Kramdown so Rouge
|
|
129
|
+
# never touches them and the content is preserved exactly for Mermaid.js.
|
|
130
|
+
text = text.gsub(/^```mermaid[ \t]*\r?\n([\s\S]*?)^```[ \t]*(\r?\n|\z)/m) do
|
|
131
|
+
"<div class=\"mermaid\">\n#{h($1.rstrip)}\n</div>\n\n"
|
|
132
|
+
end
|
|
133
|
+
|
|
128
134
|
# Process wiki links BEFORE Kramdown so that | isn't consumed as
|
|
129
135
|
# a GFM table delimiter.
|
|
130
136
|
text = text.gsub(/\[\[([^\]]+)\]\]/) do
|
data/views/layout.erb
CHANGED
|
@@ -815,6 +815,32 @@
|
|
|
815
815
|
flex-shrink: 0;
|
|
816
816
|
}
|
|
817
817
|
.link-ctx-popup-back:hover { color: #2c2c2c; }
|
|
818
|
+
.link-ctx-popup-pin {
|
|
819
|
+
background: none;
|
|
820
|
+
border: none;
|
|
821
|
+
color: #bbb;
|
|
822
|
+
cursor: pointer;
|
|
823
|
+
padding: 0 0.2rem;
|
|
824
|
+
flex-shrink: 0;
|
|
825
|
+
display: flex;
|
|
826
|
+
align-items: center;
|
|
827
|
+
line-height: 1;
|
|
828
|
+
}
|
|
829
|
+
.link-ctx-popup-pin:hover { color: #2c2c2c; }
|
|
830
|
+
.link-ctx-popup-pin.pinned { color: #8b6914; }
|
|
831
|
+
.link-ctx-popup--pinned {
|
|
832
|
+
resize: both;
|
|
833
|
+
max-height: none;
|
|
834
|
+
min-width: 280px;
|
|
835
|
+
min-height: 120px;
|
|
836
|
+
}
|
|
837
|
+
.link-ctx-popup--pinned .link-ctx-popup-header {
|
|
838
|
+
cursor: grab;
|
|
839
|
+
user-select: none;
|
|
840
|
+
}
|
|
841
|
+
.link-ctx-popup--pinned .link-ctx-popup-header.is-dragging {
|
|
842
|
+
cursor: grabbing;
|
|
843
|
+
}
|
|
818
844
|
.link-ctx-popup-body {
|
|
819
845
|
padding: 0.75rem 1rem;
|
|
820
846
|
}
|
|
@@ -829,6 +855,10 @@
|
|
|
829
855
|
margin-bottom: 0.5rem;
|
|
830
856
|
}
|
|
831
857
|
|
|
858
|
+
/* Mermaid diagrams */
|
|
859
|
+
.mermaid { text-align: center; margin: 1.2rem 0; overflow-x: auto; }
|
|
860
|
+
.mermaid svg { max-width: 100%; height: auto; }
|
|
861
|
+
|
|
832
862
|
/* Link preview popup (hover) */
|
|
833
863
|
.link-tooltip-anchor { position: relative; }
|
|
834
864
|
.link-preview-popup {
|
|
@@ -1550,12 +1580,34 @@
|
|
|
1550
1580
|
}, { passive: true });
|
|
1551
1581
|
})();
|
|
1552
1582
|
|
|
1583
|
+
// Mermaid diagram rendering — lazily loads Mermaid.js on first use
|
|
1584
|
+
var mermaidReady = false;
|
|
1585
|
+
var mermaidQueue = [];
|
|
1586
|
+
function runMermaidIn(container) {
|
|
1587
|
+
var nodes = Array.prototype.slice.call(container.querySelectorAll('.mermaid:not([data-processed="true"])'));
|
|
1588
|
+
if (!nodes.length) return;
|
|
1589
|
+
var doRender = function() { mermaid.run({ nodes: nodes }); };
|
|
1590
|
+
if (mermaidReady) { doRender(); return; }
|
|
1591
|
+
mermaidQueue.push(doRender);
|
|
1592
|
+
if (mermaidQueue.length > 1) return;
|
|
1593
|
+
var s = document.createElement('script');
|
|
1594
|
+
s.src = 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js';
|
|
1595
|
+
s.onload = function() {
|
|
1596
|
+
mermaid.initialize({ startOnLoad: false, theme: 'neutral' });
|
|
1597
|
+
mermaidReady = true;
|
|
1598
|
+
mermaidQueue.splice(0).forEach(function(fn) { fn(); });
|
|
1599
|
+
};
|
|
1600
|
+
document.head.appendChild(s);
|
|
1601
|
+
}
|
|
1602
|
+
runMermaidIn(document.body);
|
|
1603
|
+
|
|
1553
1604
|
// Left-click / tap popup for all links
|
|
1554
1605
|
(function() {
|
|
1555
1606
|
var cache = Object.create(null);
|
|
1556
1607
|
var popup = null;
|
|
1557
1608
|
var touchMoved = false;
|
|
1558
1609
|
var historyStack = [];
|
|
1610
|
+
var pinnedPopups = [];
|
|
1559
1611
|
var currentPopupPos = { x: 0, y: 0 };
|
|
1560
1612
|
var mouseLeaveTimer = null;
|
|
1561
1613
|
|
|
@@ -1584,6 +1636,7 @@
|
|
|
1584
1636
|
}
|
|
1585
1637
|
|
|
1586
1638
|
var extLinkIcon = '<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:middle;flex-shrink:0"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>';
|
|
1639
|
+
var pinIcon = '<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:middle"><line x1="12" y1="17" x2="12" y2="22"/><path d="M5 17h14v-1.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V6h1a2 2 0 0 0 0-4H8a2 2 0 0 0 0 4h1v4.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24Z"/></svg>';
|
|
1587
1640
|
|
|
1588
1641
|
function showPopup(x, y, title, bodyHtml, href, linkRect) {
|
|
1589
1642
|
// Remove old popup without clearing historyStack
|
|
@@ -1593,6 +1646,7 @@
|
|
|
1593
1646
|
|
|
1594
1647
|
popup = document.createElement('div');
|
|
1595
1648
|
popup.className = 'link-ctx-popup';
|
|
1649
|
+
var thisPopup = popup;
|
|
1596
1650
|
var backBtn = historyStack.length > 0 ? '<button class="link-ctx-popup-back" aria-label="Back">\u2190</button>' : '';
|
|
1597
1651
|
var titleHtml = href
|
|
1598
1652
|
? '<div class="link-ctx-popup-title-wrap">' +
|
|
@@ -1603,6 +1657,7 @@
|
|
|
1603
1657
|
popup.innerHTML =
|
|
1604
1658
|
'<div class="link-ctx-popup-header">' +
|
|
1605
1659
|
backBtn + titleHtml +
|
|
1660
|
+
'<button class="link-ctx-popup-pin" aria-label="Pin" title="Pin popup">' + pinIcon + '</button>' +
|
|
1606
1661
|
'<button class="link-ctx-popup-close" aria-label="Close">\u00d7</button>' +
|
|
1607
1662
|
'</div>' +
|
|
1608
1663
|
'<div class="link-ctx-popup-body">' + bodyHtml + '</div>';
|
|
@@ -1610,27 +1665,38 @@
|
|
|
1610
1665
|
|
|
1611
1666
|
clearTimeout(mouseLeaveTimer);
|
|
1612
1667
|
popup.addEventListener('mouseleave', function() {
|
|
1668
|
+
if (thisPopup.dataset.pinned) return;
|
|
1613
1669
|
mouseLeaveTimer = setTimeout(hidePopup, 150);
|
|
1614
1670
|
});
|
|
1615
1671
|
popup.addEventListener('mouseenter', function() {
|
|
1672
|
+
if (thisPopup.dataset.pinned) return;
|
|
1616
1673
|
clearTimeout(mouseLeaveTimer);
|
|
1617
1674
|
});
|
|
1618
1675
|
|
|
1619
1676
|
repositionPopup();
|
|
1677
|
+
runMermaidIn(popup);
|
|
1620
1678
|
|
|
1621
1679
|
var backBtnEl = popup.querySelector('.link-ctx-popup-back');
|
|
1622
1680
|
if (backBtnEl) {
|
|
1623
1681
|
backBtnEl.addEventListener('click', function(e) {
|
|
1624
1682
|
e.stopPropagation();
|
|
1625
1683
|
var prev = historyStack.pop();
|
|
1626
|
-
if (prev)
|
|
1684
|
+
if (prev) {
|
|
1685
|
+
showPopup(prev.x, prev.y, prev.title, prev.bodyHtml, prev.href, prev.linkRect);
|
|
1686
|
+
if (popup && prev.scrollTop) popup.scrollTop = prev.scrollTop;
|
|
1687
|
+
}
|
|
1627
1688
|
});
|
|
1628
1689
|
}
|
|
1629
1690
|
popup.querySelector('.link-ctx-popup-close').addEventListener('click', hidePopup);
|
|
1691
|
+
popup.querySelector('.link-ctx-popup-pin').addEventListener('click', function(e) {
|
|
1692
|
+
e.stopPropagation();
|
|
1693
|
+
if (!thisPopup.dataset.pinned) pinPopup(thisPopup);
|
|
1694
|
+
});
|
|
1630
1695
|
popup.addEventListener('click', function(e) { e.stopPropagation(); });
|
|
1631
1696
|
|
|
1632
1697
|
// Links in the popup body push current state to history, then open a new popup
|
|
1633
|
-
// at the same screen position as the current popup
|
|
1698
|
+
// at the same screen position as the current popup.
|
|
1699
|
+
// When the popup is pinned, links open a new floating popup at the click position instead.
|
|
1634
1700
|
var body = popup.querySelector('.link-ctx-popup-body');
|
|
1635
1701
|
body.addEventListener('click', function(e) {
|
|
1636
1702
|
var anchor = findLink(e.target);
|
|
@@ -1639,13 +1705,15 @@
|
|
|
1639
1705
|
if (!linkHref || isAnchorOnly(linkHref)) return;
|
|
1640
1706
|
e.stopPropagation();
|
|
1641
1707
|
e.preventDefault();
|
|
1708
|
+
if (thisPopup.dataset.pinned) { handleLink(anchor, e.clientX, e.clientY, false); return; }
|
|
1642
1709
|
var savedPos = { x: currentPopupPos.x, y: currentPopupPos.y, linkRect: currentPopupPos.linkRect };
|
|
1643
|
-
var titleEl =
|
|
1710
|
+
var titleEl = thisPopup.querySelector('.link-ctx-popup-title');
|
|
1644
1711
|
historyStack.push({
|
|
1645
1712
|
x: savedPos.x, y: savedPos.y, linkRect: savedPos.linkRect,
|
|
1646
1713
|
title: titleEl ? titleEl.querySelector('span').textContent : '',
|
|
1647
1714
|
bodyHtml: body.innerHTML,
|
|
1648
|
-
href: titleEl ? (titleEl.getAttribute('href') || '') : ''
|
|
1715
|
+
href: titleEl ? (titleEl.getAttribute('href') || '') : '',
|
|
1716
|
+
scrollTop: thisPopup.scrollTop
|
|
1649
1717
|
});
|
|
1650
1718
|
handleLink(anchor, savedPos.x, savedPos.y, true);
|
|
1651
1719
|
});
|
|
@@ -1657,13 +1725,15 @@
|
|
|
1657
1725
|
if (!linkHref || isAnchorOnly(linkHref)) return;
|
|
1658
1726
|
e.stopPropagation();
|
|
1659
1727
|
e.preventDefault();
|
|
1728
|
+
if (thisPopup.dataset.pinned) { handleLink(anchor, e.clientX, e.clientY, false); return; }
|
|
1660
1729
|
var savedPos = { x: currentPopupPos.x, y: currentPopupPos.y, linkRect: currentPopupPos.linkRect };
|
|
1661
|
-
var titleEl =
|
|
1730
|
+
var titleEl = thisPopup.querySelector('.link-ctx-popup-title');
|
|
1662
1731
|
historyStack.push({
|
|
1663
1732
|
x: savedPos.x, y: savedPos.y, linkRect: savedPos.linkRect,
|
|
1664
1733
|
title: titleEl ? titleEl.querySelector('span').textContent : '',
|
|
1665
1734
|
bodyHtml: body.innerHTML,
|
|
1666
|
-
href: titleEl ? (titleEl.getAttribute('href') || '') : ''
|
|
1735
|
+
href: titleEl ? (titleEl.getAttribute('href') || '') : '',
|
|
1736
|
+
scrollTop: thisPopup.scrollTop
|
|
1667
1737
|
});
|
|
1668
1738
|
handleLink(anchor, savedPos.x, savedPos.y, true);
|
|
1669
1739
|
}, { passive: false });
|
|
@@ -1673,6 +1743,80 @@
|
|
|
1673
1743
|
});
|
|
1674
1744
|
}
|
|
1675
1745
|
|
|
1746
|
+
function pinPopup(el) {
|
|
1747
|
+
clearTimeout(mouseLeaveTimer);
|
|
1748
|
+
var ownHistory = historyStack.slice();
|
|
1749
|
+
popup = null;
|
|
1750
|
+
historyStack = [];
|
|
1751
|
+
el.style.width = el.offsetWidth + 'px';
|
|
1752
|
+
el.style.height = el.offsetHeight + 'px';
|
|
1753
|
+
el.dataset.pinned = '1';
|
|
1754
|
+
el.classList.add('link-ctx-popup--pinned');
|
|
1755
|
+
var pinBtn = el.querySelector('.link-ctx-popup-pin');
|
|
1756
|
+
if (pinBtn) pinBtn.classList.add('pinned');
|
|
1757
|
+
// Re-wire back button to operate on this element's own history
|
|
1758
|
+
var backBtn = el.querySelector('.link-ctx-popup-back');
|
|
1759
|
+
if (backBtn) {
|
|
1760
|
+
var newBack = backBtn.cloneNode(true);
|
|
1761
|
+
backBtn.parentNode.replaceChild(newBack, backBtn);
|
|
1762
|
+
newBack.addEventListener('click', function(e) {
|
|
1763
|
+
e.stopPropagation();
|
|
1764
|
+
goBackInPinned(el, ownHistory);
|
|
1765
|
+
});
|
|
1766
|
+
}
|
|
1767
|
+
// Re-wire close button to remove this element
|
|
1768
|
+
var closeBtn = el.querySelector('.link-ctx-popup-close');
|
|
1769
|
+
var newClose = closeBtn.cloneNode(true);
|
|
1770
|
+
closeBtn.parentNode.replaceChild(newClose, closeBtn);
|
|
1771
|
+
newClose.addEventListener('click', function(e) {
|
|
1772
|
+
e.stopPropagation();
|
|
1773
|
+
if (el.parentNode) el.parentNode.removeChild(el);
|
|
1774
|
+
pinnedPopups = pinnedPopups.filter(function(p) { return p !== el; });
|
|
1775
|
+
});
|
|
1776
|
+
makeDraggable(el);
|
|
1777
|
+
pinnedPopups.push(el);
|
|
1778
|
+
}
|
|
1779
|
+
|
|
1780
|
+
function goBackInPinned(el, ownHistory) {
|
|
1781
|
+
var prev = ownHistory.pop();
|
|
1782
|
+
if (!prev) return;
|
|
1783
|
+
var body = el.querySelector('.link-ctx-popup-body');
|
|
1784
|
+
if (body) body.innerHTML = prev.bodyHtml;
|
|
1785
|
+
var titleSpan = el.querySelector('.link-ctx-popup-title span');
|
|
1786
|
+
if (titleSpan) titleSpan.textContent = prev.title;
|
|
1787
|
+
var titleLink = el.querySelector('a.link-ctx-popup-title');
|
|
1788
|
+
if (titleLink && prev.href) titleLink.href = prev.href;
|
|
1789
|
+
var backBtnEl = el.querySelector('.link-ctx-popup-back');
|
|
1790
|
+
if (backBtnEl) backBtnEl.style.display = ownHistory.length > 0 ? '' : 'none';
|
|
1791
|
+
if (prev.scrollTop) el.scrollTop = prev.scrollTop;
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1794
|
+
function makeDraggable(el) {
|
|
1795
|
+
var header = el.querySelector('.link-ctx-popup-header');
|
|
1796
|
+
if (!header) return;
|
|
1797
|
+
var startX, startY, startLeft, startTop;
|
|
1798
|
+
header.addEventListener('mousedown', function(e) {
|
|
1799
|
+
if (e.target.closest('button') || e.target.closest('a')) return;
|
|
1800
|
+
e.preventDefault();
|
|
1801
|
+
startX = e.clientX;
|
|
1802
|
+
startY = e.clientY;
|
|
1803
|
+
startLeft = el.offsetLeft;
|
|
1804
|
+
startTop = el.offsetTop;
|
|
1805
|
+
header.classList.add('is-dragging');
|
|
1806
|
+
document.addEventListener('mousemove', onMove);
|
|
1807
|
+
document.addEventListener('mouseup', onUp);
|
|
1808
|
+
});
|
|
1809
|
+
function onMove(e) {
|
|
1810
|
+
el.style.left = (startLeft + e.clientX - startX) + 'px';
|
|
1811
|
+
el.style.top = (startTop + e.clientY - startY) + 'px';
|
|
1812
|
+
}
|
|
1813
|
+
function onUp() {
|
|
1814
|
+
header.classList.remove('is-dragging');
|
|
1815
|
+
document.removeEventListener('mousemove', onMove);
|
|
1816
|
+
document.removeEventListener('mouseup', onUp);
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1676
1820
|
function repositionPopup() {
|
|
1677
1821
|
if (!popup) return;
|
|
1678
1822
|
var x = currentPopupPos.x, y = currentPopupPos.y;
|
|
@@ -1716,6 +1860,7 @@
|
|
|
1716
1860
|
if (body) body.innerHTML = bodyHtml;
|
|
1717
1861
|
if (title && titleTextEl) titleTextEl.textContent = title;
|
|
1718
1862
|
repositionPopup();
|
|
1863
|
+
runMermaidIn(popup);
|
|
1719
1864
|
}
|
|
1720
1865
|
|
|
1721
1866
|
function hidePopup() {
|