markdownr 0.5.16 → 0.5.18
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/bin/markdownr +5 -0
- data/lib/markdown_server/app.rb +17 -0
- data/lib/markdown_server/version.rb +1 -1
- data/views/layout.erb +113 -43
- 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: 1186498f524f4e5d50aa1d9ddb93f4d48e3dcfb327b6d4e10bb530be747179a3
|
|
4
|
+
data.tar.gz: 2d6131e668d24603309ae2b25893b3bdde2d1764f971e18acec87b1fbb26250a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 92d7e42dffe104f2c5e8a3c6289962af0ea643f27ecac103e714298b11827dd1089bd71503116bacd044cb779fca5acd192cba47d59314d991ff4d3c5514d39e
|
|
7
|
+
data.tar.gz: 8ca0350721e961e55d556bf67a9fa8a8b940e21578ac1faba965d1e9fe34968f06c4f5e5ec3ea729d7305581396be595a1fa8cd8a6664f2f97502e99cbeaf93c
|
data/bin/markdownr
CHANGED
|
@@ -43,6 +43,10 @@ OptionParser.new do |opts|
|
|
|
43
43
|
options[:hard_wrap] = false
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
+
opts.on("--verbose", "Print each request (time, IP, method, path) to stdout") do
|
|
47
|
+
options[:verbose] = true
|
|
48
|
+
end
|
|
49
|
+
|
|
46
50
|
opts.on("-v", "--version", "Show version") do
|
|
47
51
|
puts "markdownr #{MarkdownServer::VERSION}"
|
|
48
52
|
exit
|
|
@@ -64,6 +68,7 @@ MarkdownServer::App.set :allow_robots, options[:allow_robots] || false
|
|
|
64
68
|
MarkdownServer::App.set :index_file, options[:index_file]
|
|
65
69
|
MarkdownServer::App.set :link_tooltips, options.fetch(:link_tooltips, true)
|
|
66
70
|
MarkdownServer::App.set :hard_wrap, options.fetch(:hard_wrap, true)
|
|
71
|
+
MarkdownServer::App.set :verbose, options[:verbose] || false
|
|
67
72
|
MarkdownServer::App.set :port, options[:port]
|
|
68
73
|
MarkdownServer::App.set :bind, options[:bind]
|
|
69
74
|
MarkdownServer::App.set :server_settings, { max_threads: options[:threads], min_threads: 1 }
|
data/lib/markdown_server/app.rb
CHANGED
|
@@ -31,6 +31,7 @@ module MarkdownServer
|
|
|
31
31
|
set :protection, false
|
|
32
32
|
set :host_authorization, { permitted_hosts: [] }
|
|
33
33
|
set :behind_proxy, false
|
|
34
|
+
set :verbose, false
|
|
34
35
|
set :session_secret, ENV.fetch("MARKDOWNR_SESSION_SECRET", SecureRandom.hex(64))
|
|
35
36
|
set :sessions, key: "markdownr_session", same_site: :strict, httponly: true
|
|
36
37
|
end
|
|
@@ -817,6 +818,13 @@ module MarkdownServer
|
|
|
817
818
|
end
|
|
818
819
|
end
|
|
819
820
|
|
|
821
|
+
before do
|
|
822
|
+
if settings.verbose
|
|
823
|
+
$stdout.puts "#{Time.now.strftime("%Y-%m-%d %H:%M:%S")} #{client_ip} #{request.request_method} #{request.fullpath}"
|
|
824
|
+
$stdout.flush
|
|
825
|
+
end
|
|
826
|
+
end
|
|
827
|
+
|
|
820
828
|
get "/" do
|
|
821
829
|
redirect "/browse/"
|
|
822
830
|
end
|
|
@@ -1167,6 +1175,15 @@ module MarkdownServer
|
|
|
1167
1175
|
when ".epub"
|
|
1168
1176
|
redirect @download_href
|
|
1169
1177
|
|
|
1178
|
+
when ".html"
|
|
1179
|
+
send_file real_path, type: "text/html"
|
|
1180
|
+
|
|
1181
|
+
when ".css"
|
|
1182
|
+
send_file real_path, type: "text/css"
|
|
1183
|
+
|
|
1184
|
+
when ".js"
|
|
1185
|
+
send_file real_path, type: "application/javascript"
|
|
1186
|
+
|
|
1170
1187
|
else
|
|
1171
1188
|
content = File.read(real_path, encoding: "utf-8") rescue nil
|
|
1172
1189
|
if content.nil? || content.encoding == Encoding::BINARY || !content.valid_encoding?
|
data/views/layout.erb
CHANGED
|
@@ -756,16 +756,16 @@
|
|
|
756
756
|
width: 460px;
|
|
757
757
|
max-width: calc(100vw - 16px);
|
|
758
758
|
max-height: 60vh;
|
|
759
|
-
overflow-y: auto;
|
|
760
759
|
background: #faf8f4;
|
|
761
760
|
border: 1px solid #d4b96a;
|
|
762
761
|
border-radius: 6px;
|
|
763
762
|
box-shadow: 0 4px 20px rgba(0,0,0,0.22);
|
|
764
|
-
-webkit-overflow-scrolling: touch;
|
|
765
763
|
cursor: auto;
|
|
766
|
-
resize: both;
|
|
767
764
|
min-width: 280px;
|
|
768
765
|
min-height: 80px;
|
|
766
|
+
display: flex;
|
|
767
|
+
flex-direction: column;
|
|
768
|
+
overflow: hidden;
|
|
769
769
|
}
|
|
770
770
|
.link-ctx-popup-header {
|
|
771
771
|
display: flex;
|
|
@@ -774,11 +774,10 @@
|
|
|
774
774
|
gap: 0.5rem;
|
|
775
775
|
padding: 0.5rem 0.9rem;
|
|
776
776
|
border-bottom: 1px solid #e0d8c8;
|
|
777
|
-
position: sticky;
|
|
778
|
-
top: 0;
|
|
779
777
|
background: #faf8f4;
|
|
780
|
-
|
|
778
|
+
flex-shrink: 0;
|
|
781
779
|
cursor: grab;
|
|
780
|
+
touch-action: none;
|
|
782
781
|
}
|
|
783
782
|
.link-ctx-popup-header.is-dragging {
|
|
784
783
|
cursor: grabbing;
|
|
@@ -854,6 +853,22 @@
|
|
|
854
853
|
}
|
|
855
854
|
.link-ctx-popup-body {
|
|
856
855
|
padding: 0.75rem 1rem;
|
|
856
|
+
flex: 1;
|
|
857
|
+
min-height: 0;
|
|
858
|
+
overflow-y: auto;
|
|
859
|
+
-webkit-overflow-scrolling: touch;
|
|
860
|
+
}
|
|
861
|
+
.link-ctx-popup-resize {
|
|
862
|
+
position: absolute;
|
|
863
|
+
bottom: 0;
|
|
864
|
+
right: 0;
|
|
865
|
+
width: 36px;
|
|
866
|
+
height: 36px;
|
|
867
|
+
cursor: nwse-resize;
|
|
868
|
+
touch-action: none;
|
|
869
|
+
border-bottom-right-radius: 5px;
|
|
870
|
+
background: repeating-linear-gradient(-45deg, transparent, transparent 2px, rgba(0,0,0,0.18) 2px, rgba(0,0,0,0.18) 3px);
|
|
871
|
+
z-index: 2;
|
|
857
872
|
}
|
|
858
873
|
.link-ctx-popup-url {
|
|
859
874
|
font-family: "SF Mono", Menlo, Consolas, monospace;
|
|
@@ -968,7 +983,9 @@
|
|
|
968
983
|
.blb-usage li { margin-bottom: 0.15rem; }
|
|
969
984
|
.blb-usage p { margin: 0; }
|
|
970
985
|
|
|
986
|
+
|
|
971
987
|
/* Footnote tooltips */
|
|
988
|
+
sup[id^="fnref:"] { position: relative; }
|
|
972
989
|
.footnote-tooltip {
|
|
973
990
|
position: absolute;
|
|
974
991
|
bottom: 100%;
|
|
@@ -985,8 +1002,8 @@
|
|
|
985
1002
|
z-index: 150;
|
|
986
1003
|
box-shadow: 0 2px 8px rgba(0,0,0,0.25);
|
|
987
1004
|
margin-bottom: 6px;
|
|
1005
|
+
cursor: pointer;
|
|
988
1006
|
}
|
|
989
|
-
.footnote-tooltip { cursor: pointer; }
|
|
990
1007
|
.footnote-tooltip::after {
|
|
991
1008
|
content: '';
|
|
992
1009
|
position: absolute;
|
|
@@ -1001,9 +1018,6 @@
|
|
|
1001
1018
|
.footnote-tooltip a { color: #d4b96a; }
|
|
1002
1019
|
.footnote-tooltip p { margin: 0; }
|
|
1003
1020
|
.footnote-tooltip p + p { margin-top: 0.3rem; }
|
|
1004
|
-
sup[id^="fnref:"] {
|
|
1005
|
-
position: relative;
|
|
1006
|
-
}
|
|
1007
1021
|
|
|
1008
1022
|
/* Responsive */
|
|
1009
1023
|
@media (max-width: 768px) {
|
|
@@ -1412,10 +1426,6 @@
|
|
|
1412
1426
|
tooltip.style.left = '50%';
|
|
1413
1427
|
tooltip.style.transform = 'translateX(-50%)';
|
|
1414
1428
|
tooltip.style.removeProperty('--arrow-offset');
|
|
1415
|
-
// Clamp tooltip to viewport.
|
|
1416
|
-
// document.documentElement.clientWidth is more reliable than window.innerWidth
|
|
1417
|
-
// on Android browsers (e.g. EinkBro) which may return a pre-scaled layout
|
|
1418
|
-
// viewport width for window.innerWidth rather than the CSS viewport width.
|
|
1419
1429
|
var viewportWidth = Math.min(window.innerWidth, document.documentElement.clientWidth);
|
|
1420
1430
|
var rect = tooltip.getBoundingClientRect();
|
|
1421
1431
|
var shift = 0;
|
|
@@ -1439,19 +1449,16 @@
|
|
|
1439
1449
|
sup.addEventListener('mouseleave', hideTooltip);
|
|
1440
1450
|
|
|
1441
1451
|
// Touch: first tap shows tooltip, second tap navigates
|
|
1442
|
-
// Using touchend instead of click so no hover media query check is needed —
|
|
1443
|
-
// touchend only fires on real touch, regardless of what (hover:none) reports.
|
|
1444
1452
|
var linkTouchMoved = false;
|
|
1445
1453
|
link.addEventListener('touchstart', function() { linkTouchMoved = false; }, { passive: true });
|
|
1446
1454
|
link.addEventListener('touchmove', function() { linkTouchMoved = true; }, { passive: true });
|
|
1447
1455
|
link.addEventListener('touchend', function(e) {
|
|
1448
|
-
if (linkTouchMoved) return;
|
|
1456
|
+
if (linkTouchMoved) return;
|
|
1449
1457
|
if (tooltip.parentNode === sup) {
|
|
1450
1458
|
// Tooltip already showing — let the link navigate
|
|
1451
1459
|
return;
|
|
1452
1460
|
}
|
|
1453
1461
|
e.preventDefault();
|
|
1454
|
-
// Dismiss any other open tooltip
|
|
1455
1462
|
document.querySelectorAll('.footnote-tooltip').forEach(function(t) {
|
|
1456
1463
|
if (t.parentNode) t.parentNode.removeChild(t);
|
|
1457
1464
|
});
|
|
@@ -1468,7 +1475,7 @@
|
|
|
1468
1475
|
window.location.hash = fnHref;
|
|
1469
1476
|
});
|
|
1470
1477
|
|
|
1471
|
-
// Dismiss tooltip when
|
|
1478
|
+
// Dismiss tooltip when clicking elsewhere
|
|
1472
1479
|
document.addEventListener('click', function(e) {
|
|
1473
1480
|
if (tooltip.parentNode === sup && !sup.contains(e.target)) {
|
|
1474
1481
|
hideTooltip();
|
|
@@ -1672,7 +1679,8 @@
|
|
|
1672
1679
|
'<button class="link-ctx-popup-pin" aria-label="Pin" title="Pin popup">' + pinIcon + '</button>' +
|
|
1673
1680
|
'<button class="link-ctx-popup-close" aria-label="Close">\u00d7</button>' +
|
|
1674
1681
|
'</div>' +
|
|
1675
|
-
'<div class="link-ctx-popup-body">' + bodyHtml + '</div>'
|
|
1682
|
+
'<div class="link-ctx-popup-body">' + bodyHtml + '</div>' +
|
|
1683
|
+
'<div class="link-ctx-popup-resize" aria-hidden="true"></div>';
|
|
1676
1684
|
document.body.appendChild(popup);
|
|
1677
1685
|
|
|
1678
1686
|
clearTimeout(mouseLeaveTimer);
|
|
@@ -1687,13 +1695,7 @@
|
|
|
1687
1695
|
|
|
1688
1696
|
repositionPopup();
|
|
1689
1697
|
makeDraggable(popup);
|
|
1690
|
-
|
|
1691
|
-
thisPopup.addEventListener('mousedown', function lockSize() {
|
|
1692
|
-
thisPopup.style.width = thisPopup.offsetWidth + 'px';
|
|
1693
|
-
thisPopup.style.height = thisPopup.offsetHeight + 'px';
|
|
1694
|
-
thisPopup.style.maxHeight = 'none';
|
|
1695
|
-
thisPopup.removeEventListener('mousedown', lockSize);
|
|
1696
|
-
}, true);
|
|
1698
|
+
makeResizable(popup);
|
|
1697
1699
|
runMermaidIn(popup);
|
|
1698
1700
|
|
|
1699
1701
|
var backBtnEl = popup.querySelector('.link-ctx-popup-back');
|
|
@@ -1851,27 +1853,95 @@
|
|
|
1851
1853
|
var header = el.querySelector('.link-ctx-popup-header');
|
|
1852
1854
|
if (!header) return;
|
|
1853
1855
|
var startX, startY, startLeft, startTop;
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1856
|
+
|
|
1857
|
+
function onDragStart(clientX, clientY) {
|
|
1858
|
+
startX = clientX; startY = clientY;
|
|
1859
|
+
startLeft = el.offsetLeft; startTop = el.offsetTop;
|
|
1860
|
+
el.style.width = el.offsetWidth + 'px';
|
|
1861
|
+
el.style.height = el.offsetHeight + 'px';
|
|
1862
|
+
el.style.maxHeight = 'none';
|
|
1861
1863
|
popupDragging = true;
|
|
1862
1864
|
header.classList.add('is-dragging');
|
|
1863
|
-
document.addEventListener('mousemove', onMove);
|
|
1864
|
-
document.addEventListener('mouseup', onUp);
|
|
1865
|
-
});
|
|
1866
|
-
function onMove(e) {
|
|
1867
|
-
el.style.left = (startLeft + e.clientX - startX) + 'px';
|
|
1868
|
-
el.style.top = (startTop + e.clientY - startY) + 'px';
|
|
1869
1865
|
}
|
|
1870
|
-
function
|
|
1866
|
+
function onDragMove(clientX, clientY) {
|
|
1867
|
+
el.style.left = (startLeft + clientX - startX) + 'px';
|
|
1868
|
+
el.style.top = (startTop + clientY - startY) + 'px';
|
|
1869
|
+
}
|
|
1870
|
+
function onDragEnd() {
|
|
1871
1871
|
popupDragging = false;
|
|
1872
1872
|
header.classList.remove('is-dragging');
|
|
1873
|
-
|
|
1874
|
-
|
|
1873
|
+
}
|
|
1874
|
+
|
|
1875
|
+
header.addEventListener('mousedown', function(e) {
|
|
1876
|
+
if (e.target.closest('button') || e.target.closest('a')) return;
|
|
1877
|
+
e.preventDefault();
|
|
1878
|
+
onDragStart(e.clientX, e.clientY);
|
|
1879
|
+
document.addEventListener('mousemove', onMouseMove);
|
|
1880
|
+
document.addEventListener('mouseup', onMouseUp);
|
|
1881
|
+
});
|
|
1882
|
+
function onMouseMove(e) { onDragMove(e.clientX, e.clientY); }
|
|
1883
|
+
function onMouseUp() {
|
|
1884
|
+
onDragEnd();
|
|
1885
|
+
document.removeEventListener('mousemove', onMouseMove);
|
|
1886
|
+
document.removeEventListener('mouseup', onMouseUp);
|
|
1887
|
+
}
|
|
1888
|
+
|
|
1889
|
+
header.addEventListener('touchstart', function(e) {
|
|
1890
|
+
if (e.target.closest('button') || e.target.closest('a')) return;
|
|
1891
|
+
e.preventDefault();
|
|
1892
|
+
var t = e.touches[0];
|
|
1893
|
+
onDragStart(t.clientX, t.clientY);
|
|
1894
|
+
document.addEventListener('touchmove', onTouchMove, { passive: false });
|
|
1895
|
+
document.addEventListener('touchend', onTouchEnd);
|
|
1896
|
+
}, { passive: false });
|
|
1897
|
+
function onTouchMove(e) { e.preventDefault(); var t = e.touches[0]; onDragMove(t.clientX, t.clientY); }
|
|
1898
|
+
function onTouchEnd() {
|
|
1899
|
+
onDragEnd();
|
|
1900
|
+
document.removeEventListener('touchmove', onTouchMove);
|
|
1901
|
+
document.removeEventListener('touchend', onTouchEnd);
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1904
|
+
|
|
1905
|
+
function makeResizable(el) {
|
|
1906
|
+
var handle = el.querySelector('.link-ctx-popup-resize');
|
|
1907
|
+
if (!handle) return;
|
|
1908
|
+
var startX, startY, startW, startH;
|
|
1909
|
+
|
|
1910
|
+
function onResizeStart(clientX, clientY) {
|
|
1911
|
+
startX = clientX; startY = clientY;
|
|
1912
|
+
startW = el.offsetWidth; startH = el.offsetHeight;
|
|
1913
|
+
el.style.width = startW + 'px';
|
|
1914
|
+
el.style.height = startH + 'px';
|
|
1915
|
+
el.style.maxHeight = 'none';
|
|
1916
|
+
}
|
|
1917
|
+
function onResizeMove(clientX, clientY) {
|
|
1918
|
+
el.style.width = Math.max(280, startW + clientX - startX) + 'px';
|
|
1919
|
+
el.style.height = Math.max(80, startH + clientY - startY) + 'px';
|
|
1920
|
+
}
|
|
1921
|
+
|
|
1922
|
+
handle.addEventListener('mousedown', function(e) {
|
|
1923
|
+
e.preventDefault(); e.stopPropagation();
|
|
1924
|
+
onResizeStart(e.clientX, e.clientY);
|
|
1925
|
+
document.addEventListener('mousemove', onMouseMove);
|
|
1926
|
+
document.addEventListener('mouseup', onMouseUp);
|
|
1927
|
+
});
|
|
1928
|
+
function onMouseMove(e) { onResizeMove(e.clientX, e.clientY); }
|
|
1929
|
+
function onMouseUp() {
|
|
1930
|
+
document.removeEventListener('mousemove', onMouseMove);
|
|
1931
|
+
document.removeEventListener('mouseup', onMouseUp);
|
|
1932
|
+
}
|
|
1933
|
+
|
|
1934
|
+
handle.addEventListener('touchstart', function(e) {
|
|
1935
|
+
e.preventDefault(); e.stopPropagation();
|
|
1936
|
+
var t = e.touches[0];
|
|
1937
|
+
onResizeStart(t.clientX, t.clientY);
|
|
1938
|
+
document.addEventListener('touchmove', onTouchMove, { passive: false });
|
|
1939
|
+
document.addEventListener('touchend', onTouchEnd);
|
|
1940
|
+
}, { passive: false });
|
|
1941
|
+
function onTouchMove(e) { e.preventDefault(); onResizeMove(e.touches[0].clientX, e.touches[0].clientY); }
|
|
1942
|
+
function onTouchEnd() {
|
|
1943
|
+
document.removeEventListener('touchmove', onTouchMove);
|
|
1944
|
+
document.removeEventListener('touchend', onTouchEnd);
|
|
1875
1945
|
}
|
|
1876
1946
|
}
|
|
1877
1947
|
|