joplin-plugin-minimap 1.1.1 → 1.1.3
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/package.json +1 -1
- package/publish/manifest.json +1 -1
- package/publish/minimap-view.js +68 -5
- package/publish/minimap.css +5 -6
- package/publish/plugin.jpl +0 -0
package/package.json
CHANGED
package/publish/manifest.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"manifest_version": 1,
|
|
3
3
|
"id": "com.github.joplin-minimap",
|
|
4
4
|
"app_min_version": "2.8",
|
|
5
|
-
"version": "1.1.
|
|
5
|
+
"version": "1.1.3",
|
|
6
6
|
"name": "Joplin Minimap",
|
|
7
7
|
"description": "A hover-to-expand table of contents minimap for the note viewer. Shows heading tick marks on the right edge; hover to expand into a full ToC, click to jump.",
|
|
8
8
|
"author": "lim0513",
|
package/publish/minimap-view.js
CHANGED
|
@@ -25,6 +25,47 @@
|
|
|
25
25
|
|
|
26
26
|
var cleanup = null; // removes listeners belonging to the current build
|
|
27
27
|
|
|
28
|
+
// Clicking the minimap gives the webview focus, which can make Joplin
|
|
29
|
+
// re-render the whole note. That detaches every heading element we hold,
|
|
30
|
+
// and scrollIntoView on a detached node is a silent no-op. So jumps are
|
|
31
|
+
// index/text-based against the LIVE DOM, and if a rebuild happens right
|
|
32
|
+
// after a click (the re-render case), the jump is re-applied afterwards.
|
|
33
|
+
var pendingJump = null; // { index, text, until }
|
|
34
|
+
|
|
35
|
+
function liveHeadings() {
|
|
36
|
+
var root = document.getElementById('rendered-md') || document.body;
|
|
37
|
+
return Array.prototype.slice.call(root.querySelectorAll('h1, h2, h3, h4, h5, h6'));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function jumpTo(index, text) {
|
|
41
|
+
var hs = liveHeadings();
|
|
42
|
+
var h = hs[index];
|
|
43
|
+
if (!h || (text && (h.textContent || '').trim() !== text)) {
|
|
44
|
+
for (var i = 0; i < hs.length; i++) {
|
|
45
|
+
if ((hs[i].textContent || '').trim() === text) { h = hs[i]; break; }
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (h && h.isConnected) h.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Width of the note viewer's right-edge scrollbar zone. Overlay scrollbars
|
|
52
|
+
// (e.g. Windows 11) reserve no layout space but still intercept clicks with
|
|
53
|
+
// priority over page content, so when nothing is measurable we keep a safety
|
|
54
|
+
// gap anyway. The minimap must stay clear of that zone or clicks on the
|
|
55
|
+
// rightmost part of the panel silently hit the scrollbar instead.
|
|
56
|
+
function scrollbarGap(root) {
|
|
57
|
+
var gap = 0;
|
|
58
|
+
var node = root;
|
|
59
|
+
while (node && node !== document.documentElement) {
|
|
60
|
+
if (node.scrollHeight > node.clientHeight + 1) {
|
|
61
|
+
var w = node.offsetWidth - node.clientWidth;
|
|
62
|
+
if (w > gap) gap = w;
|
|
63
|
+
}
|
|
64
|
+
node = node.parentElement;
|
|
65
|
+
}
|
|
66
|
+
return gap > 0 ? gap : 14;
|
|
67
|
+
}
|
|
68
|
+
|
|
28
69
|
function build() {
|
|
29
70
|
if (cleanup) { cleanup(); cleanup = null; }
|
|
30
71
|
|
|
@@ -39,12 +80,18 @@
|
|
|
39
80
|
|
|
40
81
|
var nav = document.createElement('nav');
|
|
41
82
|
nav.id = 'jp-minimap';
|
|
42
|
-
|
|
83
|
+
// Applied in BOTH collapsed and expanded states: shifting only on hover
|
|
84
|
+
// would move the panel out from under the cursor and cause a
|
|
85
|
+
// hover/unhover flicker loop.
|
|
86
|
+
nav.style.right = (settings.rightOffset + scrollbarGap(root)) + 'px';
|
|
43
87
|
nav.style.setProperty('--jp-mm-width', settings.panelWidth + 'px');
|
|
44
88
|
// The viewer DOM can be editable in some contexts; make sure the
|
|
45
89
|
// minimap never shows a caret or accepts keyboard input.
|
|
90
|
+
// NOTE: no preventDefault on mousedown! Blocking the default mouse-focus
|
|
91
|
+
// path makes Joplin's later programmatic focus count as keyboard-like,
|
|
92
|
+
// and the browser then draws a :focus-visible ring on the scroll
|
|
93
|
+
// container. Natural mouse focus never shows a ring.
|
|
46
94
|
nav.setAttribute('contenteditable', 'false');
|
|
47
|
-
nav.addEventListener('mousedown', function (e) { e.preventDefault(); });
|
|
48
95
|
// Own the wheel entirely while the cursor is over the minimap:
|
|
49
96
|
// scroll the ToC list ourselves and never let the event chain
|
|
50
97
|
// through to the note underneath (scroll chaining feels erratic).
|
|
@@ -56,7 +103,7 @@
|
|
|
56
103
|
var list = document.createElement('div');
|
|
57
104
|
list.className = 'jp-mm-list';
|
|
58
105
|
|
|
59
|
-
var items = headings.map(function (h) {
|
|
106
|
+
var items = headings.map(function (h, index) {
|
|
60
107
|
var level = Number(h.tagName.charAt(1));
|
|
61
108
|
|
|
62
109
|
// NOT an <a>: Joplin's viewer shows a "Ctrl+click to open" tooltip
|
|
@@ -74,8 +121,17 @@
|
|
|
74
121
|
item.appendChild(bar);
|
|
75
122
|
item.appendChild(label);
|
|
76
123
|
|
|
77
|
-
|
|
78
|
-
|
|
124
|
+
// MOUSEDOWN, not click: the press gives the webview focus, Joplin may
|
|
125
|
+
// re-render the note, and our panel gets rebuilt BETWEEN mousedown and
|
|
126
|
+
// mouseup - so the click event (which needs the same target for both)
|
|
127
|
+
// never fires. mousedown runs before any of that can happen.
|
|
128
|
+
item.addEventListener('mousedown', function (e) {
|
|
129
|
+
if (e.button !== 0) return;
|
|
130
|
+
var text = (label.textContent || '').trim();
|
|
131
|
+
// If the press triggers a note re-render, the rebuild will
|
|
132
|
+
// re-apply this jump against the fresh DOM.
|
|
133
|
+
pendingJump = { index: index, text: text, until: Date.now() + 1200 };
|
|
134
|
+
jumpTo(index, text);
|
|
79
135
|
});
|
|
80
136
|
|
|
81
137
|
list.appendChild(item);
|
|
@@ -98,6 +154,13 @@
|
|
|
98
154
|
document.addEventListener('scroll', updateActive, { passive: true, capture: true });
|
|
99
155
|
updateActive();
|
|
100
156
|
|
|
157
|
+
// A rebuild arriving right after a click means the note was re-rendered
|
|
158
|
+
// and the original scrollIntoView hit a detached node - redo the jump.
|
|
159
|
+
if (pendingJump && Date.now() < pendingJump.until) {
|
|
160
|
+
jumpTo(pendingJump.index, pendingJump.text);
|
|
161
|
+
pendingJump = null;
|
|
162
|
+
}
|
|
163
|
+
|
|
101
164
|
cleanup = function () {
|
|
102
165
|
document.removeEventListener('scroll', updateActive, { capture: true });
|
|
103
166
|
};
|
package/publish/minimap.css
CHANGED
|
@@ -103,12 +103,11 @@
|
|
|
103
103
|
background: rgba(127, 127, 127, 0.18);
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
/*
|
|
107
|
-
|
|
108
|
-
.
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
106
|
+
/* No scrollbar in the expanded panel: the wheel handler owns scrolling,
|
|
107
|
+
* and a visible scrollbar at the panel edge invites overlay-scrollbar
|
|
108
|
+
* style hover/click interference. */
|
|
109
|
+
.jp-mm-list::-webkit-scrollbar { display: none; }
|
|
110
|
+
.jp-mm-list { scrollbar-width: none; }
|
|
112
111
|
|
|
113
112
|
/* don't show over printed/exported output */
|
|
114
113
|
@media print {
|
package/publish/plugin.jpl
CHANGED
|
Binary file
|