markdownr 0.3.3 → 0.3.5
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 +22 -2
- data/lib/markdown_server/version.rb +1 -1
- data/views/layout.erb +70 -46
- data/views/markdown.erb +3 -14
- 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: a6f6186e83f09bb4239d8ca1fd5cca349fbbe17ccea6565a5579c11920e7ce2e
|
|
4
|
+
data.tar.gz: 1eb3bc2813a2748ad37b63271e3e6844b4f927dc638f0c0bd1d0a7d65f940e28
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c249628b120b2af78fe64e9690362aa2978896cf098fc9aec04f7225f685661203f3d5dd59bde5b8ddf3bb6c959ac92a4c09f5b46483f121a9355bdf7675e68c
|
|
7
|
+
data.tar.gz: 80774310a8da3df52c41ef1e7ea811040a85392d5b6d02e149e1b3be0d12bc63d1f4bab5b7972f0a173da2a118f44aaf932df4c6e11487a62ffc7fe5fd47b548
|
data/lib/markdown_server/app.rb
CHANGED
|
@@ -192,7 +192,27 @@ module MarkdownServer
|
|
|
192
192
|
def render_frontmatter_value(value)
|
|
193
193
|
case value
|
|
194
194
|
when Array
|
|
195
|
-
value.map { |v|
|
|
195
|
+
value.map { |v|
|
|
196
|
+
str = v.to_s
|
|
197
|
+
if str =~ /\A\[\[([^\]]+)\]\]\z/
|
|
198
|
+
raw = $1
|
|
199
|
+
target, display = raw.include?("|") ? raw.split("|", 2) : [raw, nil]
|
|
200
|
+
label = display || target
|
|
201
|
+
if target.start_with?("#")
|
|
202
|
+
anchor = target[1..].downcase.gsub(/\s+/, "-").gsub(/[^\w-]/, "")
|
|
203
|
+
%(<a class="wiki-link" href="##{h(anchor)}">#{h(label)}</a>)
|
|
204
|
+
else
|
|
205
|
+
resolved = resolve_wiki_link(target)
|
|
206
|
+
if resolved
|
|
207
|
+
%(<a class="wiki-link" href="/browse/#{encode_path_component(resolved).gsub('%2F', '/')}">#{h(label)}</a>)
|
|
208
|
+
else
|
|
209
|
+
%(<span class="wiki-link broken">#{h(label)}</span>)
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
else
|
|
213
|
+
%(<span class="tag">#{h(str)}</span>)
|
|
214
|
+
end
|
|
215
|
+
}.join(" ")
|
|
196
216
|
when String
|
|
197
217
|
if value =~ /\Ahttps?:\/\//
|
|
198
218
|
%(<a href="#{h(value)}" target="_blank" rel="noopener">#{h(value)}</a>)
|
|
@@ -219,7 +239,7 @@ module MarkdownServer
|
|
|
219
239
|
def extract_toc(html)
|
|
220
240
|
headings = []
|
|
221
241
|
html.scan(/<h([1-6])\s[^>]*id="([^"]*)"[^>]*>(.*?)<\/h\1>/mi) do |level, id, text|
|
|
222
|
-
clean_text = text.gsub(/<[^>]+>/, "").strip
|
|
242
|
+
clean_text = text.gsub(/<sup[^>]*id="fnref:[^"]*"[^>]*>.*?<\/sup>/i, "").gsub(/<[^>]+>/, "").strip
|
|
223
243
|
headings << { level: level.to_i, id: id, text: clean_text }
|
|
224
244
|
end
|
|
225
245
|
headings
|
data/views/layout.erb
CHANGED
|
@@ -113,14 +113,13 @@
|
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
/* Frontmatter */
|
|
116
|
-
|
|
116
|
+
div.frontmatter {
|
|
117
117
|
margin-bottom: 1.5rem;
|
|
118
118
|
border: 1px solid #e0d8c8;
|
|
119
119
|
border-radius: 6px;
|
|
120
120
|
background: #fdfcf9;
|
|
121
121
|
}
|
|
122
|
-
|
|
123
|
-
cursor: pointer;
|
|
122
|
+
.frontmatter-heading {
|
|
124
123
|
padding: 0.6rem 1rem;
|
|
125
124
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
|
|
126
125
|
font-size: 0.85rem;
|
|
@@ -128,9 +127,6 @@
|
|
|
128
127
|
color: #8b6914;
|
|
129
128
|
background: #f5f0e4;
|
|
130
129
|
border-radius: 6px 6px 0 0;
|
|
131
|
-
user-select: none;
|
|
132
|
-
}
|
|
133
|
-
details.frontmatter[open] summary {
|
|
134
130
|
border-bottom: 1px solid #e0d8c8;
|
|
135
131
|
}
|
|
136
132
|
.meta-table {
|
|
@@ -403,40 +399,6 @@
|
|
|
403
399
|
min-width: 0;
|
|
404
400
|
}
|
|
405
401
|
|
|
406
|
-
/* Mobile TOC (replaces sidebar) */
|
|
407
|
-
.toc-mobile {
|
|
408
|
-
display: none;
|
|
409
|
-
margin-bottom: 1rem;
|
|
410
|
-
border: 1px solid #e0d8c8;
|
|
411
|
-
border-radius: 6px;
|
|
412
|
-
background: #fdfcf9;
|
|
413
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
|
|
414
|
-
font-size: 0.82rem;
|
|
415
|
-
}
|
|
416
|
-
.toc-mobile summary {
|
|
417
|
-
cursor: pointer;
|
|
418
|
-
padding: 0.5rem 0.8rem;
|
|
419
|
-
font-weight: 600;
|
|
420
|
-
color: #8b6914;
|
|
421
|
-
font-size: 0.8rem;
|
|
422
|
-
user-select: none;
|
|
423
|
-
}
|
|
424
|
-
.toc-mobile ul {
|
|
425
|
-
list-style: none;
|
|
426
|
-
padding: 0 0.8rem 0.5rem;
|
|
427
|
-
margin: 0;
|
|
428
|
-
}
|
|
429
|
-
.toc-mobile li { margin-bottom: 0.2rem; }
|
|
430
|
-
.toc-mobile a {
|
|
431
|
-
color: #666;
|
|
432
|
-
text-decoration: none;
|
|
433
|
-
padding: 0.2rem 0;
|
|
434
|
-
display: block;
|
|
435
|
-
}
|
|
436
|
-
.toc-mobile a:hover { color: #8b6914; }
|
|
437
|
-
.toc-mobile .toc-h3 { padding-left: 0.8rem; }
|
|
438
|
-
.toc-mobile .toc-h4 { padding-left: 1.4rem; }
|
|
439
|
-
|
|
440
402
|
/* TOC drawer (swipe-to-reveal on mobile) */
|
|
441
403
|
.toc-overlay {
|
|
442
404
|
display: none;
|
|
@@ -731,15 +693,15 @@
|
|
|
731
693
|
max-width: 350px;
|
|
732
694
|
width: max-content;
|
|
733
695
|
z-index: 150;
|
|
734
|
-
pointer-events: none;
|
|
735
696
|
box-shadow: 0 2px 8px rgba(0,0,0,0.25);
|
|
736
697
|
margin-bottom: 6px;
|
|
737
698
|
}
|
|
699
|
+
.footnote-tooltip { cursor: pointer; }
|
|
738
700
|
.footnote-tooltip::after {
|
|
739
701
|
content: '';
|
|
740
702
|
position: absolute;
|
|
741
703
|
top: 100%;
|
|
742
|
-
left: 50
|
|
704
|
+
left: calc(50% + var(--arrow-offset, 0px));
|
|
743
705
|
transform: translateX(-50%);
|
|
744
706
|
border: 5px solid transparent;
|
|
745
707
|
border-top-color: #2d2d2d;
|
|
@@ -768,7 +730,6 @@
|
|
|
768
730
|
|
|
769
731
|
.page-with-toc { display: block; }
|
|
770
732
|
.toc-sidebar { display: none; }
|
|
771
|
-
.toc-mobile { display: block; }
|
|
772
733
|
|
|
773
734
|
.meta-table th, .meta-table td {
|
|
774
735
|
display: block;
|
|
@@ -1093,12 +1054,75 @@
|
|
|
1093
1054
|
var tooltip = document.createElement('span');
|
|
1094
1055
|
tooltip.className = 'footnote-tooltip';
|
|
1095
1056
|
tooltip.innerHTML = html;
|
|
1057
|
+
var fnHref = link.getAttribute('href');
|
|
1096
1058
|
|
|
1097
|
-
|
|
1059
|
+
function showTooltip() {
|
|
1098
1060
|
sup.appendChild(tooltip);
|
|
1099
|
-
|
|
1100
|
-
|
|
1061
|
+
// Reset any previous adjustment
|
|
1062
|
+
tooltip.style.left = '50%';
|
|
1063
|
+
tooltip.style.transform = 'translateX(-50%)';
|
|
1064
|
+
tooltip.style.removeProperty('--arrow-offset');
|
|
1065
|
+
// Clamp tooltip to viewport.
|
|
1066
|
+
// document.documentElement.clientWidth is more reliable than window.innerWidth
|
|
1067
|
+
// on Android browsers (e.g. EinkBro) which may return a pre-scaled layout
|
|
1068
|
+
// viewport width for window.innerWidth rather than the CSS viewport width.
|
|
1069
|
+
var viewportWidth = Math.min(window.innerWidth, document.documentElement.clientWidth);
|
|
1070
|
+
var rect = tooltip.getBoundingClientRect();
|
|
1071
|
+
var shift = 0;
|
|
1072
|
+
if (rect.left < 8) {
|
|
1073
|
+
shift = 8 - rect.left;
|
|
1074
|
+
} else if (rect.right > viewportWidth - 8) {
|
|
1075
|
+
shift = (viewportWidth - 8) - rect.right;
|
|
1076
|
+
}
|
|
1077
|
+
if (shift !== 0) {
|
|
1078
|
+
tooltip.style.transform = 'translateX(calc(-50% + ' + shift + 'px))';
|
|
1079
|
+
tooltip.style.setProperty('--arrow-offset', (-shift) + 'px');
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
function hideTooltip() {
|
|
1101
1084
|
if (tooltip.parentNode === sup) sup.removeChild(tooltip);
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
// Hover devices: show/hide on mouseenter/mouseleave
|
|
1088
|
+
sup.addEventListener('mouseenter', showTooltip);
|
|
1089
|
+
sup.addEventListener('mouseleave', hideTooltip);
|
|
1090
|
+
|
|
1091
|
+
// Touch: first tap shows tooltip, second tap navigates
|
|
1092
|
+
// Using touchend instead of click so no hover media query check is needed —
|
|
1093
|
+
// touchend only fires on real touch, regardless of what (hover:none) reports.
|
|
1094
|
+
var linkTouchMoved = false;
|
|
1095
|
+
link.addEventListener('touchstart', function() { linkTouchMoved = false; }, { passive: true });
|
|
1096
|
+
link.addEventListener('touchmove', function() { linkTouchMoved = true; }, { passive: true });
|
|
1097
|
+
link.addEventListener('touchend', function(e) {
|
|
1098
|
+
if (linkTouchMoved) return; // scroll, not a tap
|
|
1099
|
+
if (tooltip.parentNode === sup) {
|
|
1100
|
+
// Tooltip already showing — let the link navigate
|
|
1101
|
+
return;
|
|
1102
|
+
}
|
|
1103
|
+
e.preventDefault();
|
|
1104
|
+
// Dismiss any other open tooltip
|
|
1105
|
+
document.querySelectorAll('.footnote-tooltip').forEach(function(t) {
|
|
1106
|
+
if (t.parentNode) t.parentNode.removeChild(t);
|
|
1107
|
+
});
|
|
1108
|
+
showTooltip();
|
|
1109
|
+
});
|
|
1110
|
+
|
|
1111
|
+
var tooltipTouchMoved = false;
|
|
1112
|
+
tooltip.addEventListener('touchstart', function() { tooltipTouchMoved = false; }, { passive: true });
|
|
1113
|
+
tooltip.addEventListener('touchmove', function() { tooltipTouchMoved = true; }, { passive: true });
|
|
1114
|
+
tooltip.addEventListener('touchend', function(e) {
|
|
1115
|
+
if (tooltipTouchMoved) return;
|
|
1116
|
+
e.stopPropagation();
|
|
1117
|
+
hideTooltip();
|
|
1118
|
+
window.location.hash = fnHref;
|
|
1119
|
+
});
|
|
1120
|
+
|
|
1121
|
+
// Dismiss tooltip when tapping elsewhere
|
|
1122
|
+
document.addEventListener('click', function(e) {
|
|
1123
|
+
if (tooltip.parentNode === sup && !sup.contains(e.target)) {
|
|
1124
|
+
hideTooltip();
|
|
1125
|
+
}
|
|
1102
1126
|
});
|
|
1103
1127
|
});
|
|
1104
1128
|
})();
|
data/views/markdown.erb
CHANGED
|
@@ -11,17 +11,6 @@
|
|
|
11
11
|
</div>
|
|
12
12
|
|
|
13
13
|
<% if @has_toc %>
|
|
14
|
-
<details class="toc-mobile">
|
|
15
|
-
<summary>Table of Contents</summary>
|
|
16
|
-
<ul>
|
|
17
|
-
<% @toc.each do |entry| %>
|
|
18
|
-
<li class="toc-h<%= entry[:level] %>">
|
|
19
|
-
<a href="#<%= h(entry[:id]) %>"><%= h(entry[:text]) %></a>
|
|
20
|
-
</li>
|
|
21
|
-
<% end %>
|
|
22
|
-
</ul>
|
|
23
|
-
</details>
|
|
24
|
-
|
|
25
14
|
<button class="toc-fab" id="toc-fab" aria-label="Table of Contents">☰</button>
|
|
26
15
|
<div class="toc-overlay" id="toc-overlay"></div>
|
|
27
16
|
<nav class="toc-drawer" id="toc-drawer">
|
|
@@ -55,8 +44,8 @@
|
|
|
55
44
|
|
|
56
45
|
<div class="page-main">
|
|
57
46
|
<% if @meta && !@meta.empty? %>
|
|
58
|
-
<
|
|
59
|
-
<
|
|
47
|
+
<div class="frontmatter">
|
|
48
|
+
<div class="frontmatter-heading">Frontmatter</div>
|
|
60
49
|
<table class="meta-table">
|
|
61
50
|
<% @meta.each do |key, value| %>
|
|
62
51
|
<tr>
|
|
@@ -65,7 +54,7 @@
|
|
|
65
54
|
</tr>
|
|
66
55
|
<% end %>
|
|
67
56
|
</table>
|
|
68
|
-
</
|
|
57
|
+
</div>
|
|
69
58
|
<% end %>
|
|
70
59
|
|
|
71
60
|
<div class="md-content" data-file="<%= h(@title) %>">
|