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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9701699dcaa5bb10ddddcff543dace0bc75184e86529ac048efbb0337e61b63c
4
- data.tar.gz: 5156b2929f5c6195007c2755d387c6df17f0b33e8ca162045bad9f0618bec62a
3
+ metadata.gz: 1186498f524f4e5d50aa1d9ddb93f4d48e3dcfb327b6d4e10bb530be747179a3
4
+ data.tar.gz: 2d6131e668d24603309ae2b25893b3bdde2d1764f971e18acec87b1fbb26250a
5
5
  SHA512:
6
- metadata.gz: d77b2e509c1516c496f05f134f342d083d5e6c007d24796dfe370f8f0884aca81f62885e02b111f222e630e28d644674a6839225c711219d3dee702c4dd93faf
7
- data.tar.gz: e7dd49dc45652b516b688e4066c0577cfb4ed39f5e7bb314de40366d85680e82403a74d6bf244ce75ce6228f157c7c47a960667c2de0c46fed6696fd83b64e86
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 }
@@ -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?
@@ -1,3 +1,3 @@
1
1
  module MarkdownServer
2
- VERSION = "0.5.16"
2
+ VERSION = "0.5.18"
3
3
  end
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
- z-index: 1;
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; // scroll, not a tap
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 tapping elsewhere
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
- // Lock explicit dimensions on first interaction so resize can grow past max-height
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
- header.addEventListener('mousedown', function(e) {
1855
- if (e.target.closest('button') || e.target.closest('a')) return;
1856
- e.preventDefault();
1857
- startX = e.clientX;
1858
- startY = e.clientY;
1859
- startLeft = el.offsetLeft;
1860
- startTop = el.offsetTop;
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 onUp() {
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
- document.removeEventListener('mousemove', onMove);
1874
- document.removeEventListener('mouseup', onUp);
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
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: markdownr
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.16
4
+ version: 0.5.18
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Dunn