joplin-plugin-minimap 1.1.3 → 1.1.6

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "joplin-plugin-minimap",
3
- "version": "1.1.3",
3
+ "version": "1.1.6",
4
4
  "description": "A hover-to-expand table of contents minimap for the Joplin note viewer",
5
5
  "author": "lim0513",
6
6
  "homepage": "https://github.com/lim0513/joplin-minimap",
@@ -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.3",
5
+ "version": "1.1.6",
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",
@@ -19,12 +19,44 @@
19
19
  }).catch(function () { /* keep defaults (e.g. print/export context) */ });
20
20
  }
21
21
 
22
+ // NEVER run inside an editable context (the Rich Text editor renders
23
+ // notes through the same pipeline and would execute this asset). If the
24
+ // nav lands in the RTE document, Joplin's HTML->markdown round-trip
25
+ // SERIALIZES it into the note body on save - the heading list becomes
26
+ // real note content and syncs to every device. This was the true root
27
+ // cause of the "outline below the document" reports.
28
+ function isEditableContext() {
29
+ var b = document.body;
30
+ if (!b) return false;
31
+ if (b.isContentEditable) return true;
32
+ if (b.id === 'tinymce') return true;
33
+ if (b.classList && b.classList.contains('mce-content-body')) return true;
34
+ return false;
35
+ }
36
+ if (isEditableContext()) return;
37
+
22
38
  // Only ever install one instance of the watcher per webview session.
23
39
  if (window.__jpMinimapInstalled) return;
24
40
  window.__jpMinimapInstalled = true;
25
41
 
26
42
  var cleanup = null; // removes listeners belonging to the current build
27
43
 
44
+ // The stylesheet is injected BY THIS SCRIPT and re-checked on every build.
45
+ // Rationale: Joplin can re-render the document in ways that drop injected
46
+ // asset stylesheets while this script's watcher survives - the rebuilt nav
47
+ // then renders UNSTYLED as flow content below the note (looks like a
48
+ // duplicated outline under the document). Keeping the CSS inline and
49
+ // re-injecting guarantees the nav and its styling live and die together.
50
+ var MINIMAP_CSS = "/* Joplin Minimap \u2014 collapsed tick bars, hover-expanded ToC panel.\n * Colors use currentColor / rgba so it follows both light and dark themes.\n */\n\n#jp-minimap {\n\tuser-select: none;\n\t-webkit-user-select: none;\n\tcaret-color: transparent;\n\tcursor: default;\n\tposition: fixed;\n\ttop: 50%;\n\tright: 6px;\n\ttransform: translateY(-50%);\n\tz-index: 9999;\n\tfont-size: 12.5px;\n\tline-height: 1.35;\n\tcolor: inherit;\n}\n\n.jp-mm-list {\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: flex-end;\n\tpadding: 8px 6px;\n\tmax-height: 84vh;\n\toverflow: hidden;\n\tborder-radius: 10px;\n\ttransition: background 0.15s ease, box-shadow 0.15s ease;\n}\n\n.jp-mm-item {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: flex-end;\n\tpadding: 3px 4px;\n\tborder-radius: 6px;\n\ttext-decoration: none;\n\tcolor: inherit;\n\topacity: 0.5;\n\tcursor: pointer;\n\toutline: none;\n}\n\n/* ---- collapsed state: tick bars, width by heading level ---- */\n\n.jp-mm-bar {\n\tdisplay: block;\n\theight: 2px;\n\tborder-radius: 1px;\n\tbackground: currentColor;\n}\n\n.jp-mm-l1 .jp-mm-bar { width: 18px; }\n.jp-mm-l2 .jp-mm-bar { width: 13px; }\n.jp-mm-l3 .jp-mm-bar { width: 9px; }\n.jp-mm-l4 .jp-mm-bar { width: 7px; }\n.jp-mm-l5 .jp-mm-bar { width: 5px; }\n.jp-mm-l6 .jp-mm-bar { width: 5px; }\n\n.jp-mm-label { display: none; }\n\n/* ---- expanded state (hover) ---- */\n\n#jp-minimap:hover .jp-mm-list {\n\talign-items: stretch;\n\toverflow-y: auto;\n\toverscroll-behavior: contain;\n\tbackground: rgba(127, 127, 127, 0.16);\n\tbackdrop-filter: blur(10px);\n\t-webkit-backdrop-filter: blur(10px);\n\tbox-shadow: 0 6px 28px rgba(0, 0, 0, 0.28);\n}\n\n#jp-minimap:hover .jp-mm-bar { display: none; }\n\n#jp-minimap:hover .jp-mm-item { justify-content: flex-start; }\n\n#jp-minimap:hover .jp-mm-label {\n\tdisplay: block;\n\tmax-width: var(--jp-mm-width, 240px);\n\twhite-space: nowrap;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n}\n\n/* indent by heading level when expanded */\n#jp-minimap:hover .jp-mm-l2 { padding-left: 16px; }\n#jp-minimap:hover .jp-mm-l3 { padding-left: 28px; }\n#jp-minimap:hover .jp-mm-l4 { padding-left: 40px; }\n#jp-minimap:hover .jp-mm-l5 { padding-left: 52px; }\n#jp-minimap:hover .jp-mm-l6 { padding-left: 52px; }\n\n/* ---- shared states ---- */\n\n.jp-mm-item:hover {\n\topacity: 1;\n\tbackground: rgba(127, 127, 127, 0.22);\n}\n\n.jp-mm-active { opacity: 1; }\n\n#jp-minimap:hover .jp-mm-active {\n\tbackground: rgba(127, 127, 127, 0.18);\n}\n\n/* No scrollbar in the expanded panel: the wheel handler owns scrolling,\n * and a visible scrollbar at the panel edge invites overlay-scrollbar\n * style hover/click interference. */\n.jp-mm-list::-webkit-scrollbar { display: none; }\n.jp-mm-list { scrollbar-width: none; }\n\n/* don't show over printed/exported output */\n@media print {\n\t#jp-minimap { display: none; }\n}\n";
51
+
52
+ function ensureStyle() {
53
+ if (document.getElementById('jp-minimap-style')) return;
54
+ var styleEl = document.createElement('style');
55
+ styleEl.id = 'jp-minimap-style';
56
+ styleEl.textContent = MINIMAP_CSS;
57
+ (document.head || document.documentElement).appendChild(styleEl);
58
+ }
59
+
28
60
  // Clicking the minimap gives the webview focus, which can make Joplin
29
61
  // re-render the whole note. That detaches every heading element we hold,
30
62
  // and scrollIntoView on a detached node is a silent no-op. So jumps are
@@ -68,6 +100,12 @@
68
100
 
69
101
  function build() {
70
102
  if (cleanup) { cleanup(); cleanup = null; }
103
+ if (isEditableContext()) {
104
+ var stale = document.getElementById('jp-minimap');
105
+ if (stale) stale.remove();
106
+ return;
107
+ }
108
+ ensureStyle();
71
109
 
72
110
  var old = document.getElementById('jp-minimap');
73
111
  if (old) old.remove();
@@ -191,6 +229,16 @@
191
229
  // Joplin fires this after each note render/update.
192
230
  document.addEventListener('joplin-noteDidUpdate', scheduleBuild);
193
231
 
232
+ // Joplin's asset cleanup can remove our <style> from <head> AFTER the
233
+ // last body mutation - nothing rebuilds, and the nav sits unstyled in
234
+ // the page (the "outline below the document" bug, second incarnation:
235
+ // v1.1.4 only re-checked the style during rebuilds). Watch the head
236
+ // and re-inject immediately.
237
+ var headObserver = new MutationObserver(function () {
238
+ if (!document.getElementById('jp-minimap-style')) ensureStyle();
239
+ });
240
+ if (document.head) headObserver.observe(document.head, { childList: true });
241
+
194
242
  // Fallback: watch for the rendered content being swapped out
195
243
  // (note switch replaces the DOM without re-running this script).
196
244
  var mo = new MutationObserver(function (mutations) {
@@ -10,8 +10,10 @@ module.exports = {
10
10
  // intentionally empty — rendering is untouched
11
11
  },
12
12
  assets: function () {
13
+ // CSS is injected by minimap-view.js itself (see ensureStyle) so
14
+ // that a re-render which drops asset stylesheets cannot leave an
15
+ // unstyled nav in the document flow.
13
16
  return [
14
- { name: 'minimap.css' },
15
17
  { name: 'minimap-view.js' },
16
18
  ];
17
19
  },
Binary file
@@ -1,115 +0,0 @@
1
- /* Joplin Minimap — collapsed tick bars, hover-expanded ToC panel.
2
- * Colors use currentColor / rgba so it follows both light and dark themes.
3
- */
4
-
5
- #jp-minimap {
6
- user-select: none;
7
- -webkit-user-select: none;
8
- caret-color: transparent;
9
- cursor: default;
10
- position: fixed;
11
- top: 50%;
12
- right: 6px;
13
- transform: translateY(-50%);
14
- z-index: 9999;
15
- font-size: 12.5px;
16
- line-height: 1.35;
17
- color: inherit;
18
- }
19
-
20
- .jp-mm-list {
21
- display: flex;
22
- flex-direction: column;
23
- align-items: flex-end;
24
- padding: 8px 6px;
25
- max-height: 84vh;
26
- overflow: hidden;
27
- border-radius: 10px;
28
- transition: background 0.15s ease, box-shadow 0.15s ease;
29
- }
30
-
31
- .jp-mm-item {
32
- display: flex;
33
- align-items: center;
34
- justify-content: flex-end;
35
- padding: 3px 4px;
36
- border-radius: 6px;
37
- text-decoration: none;
38
- color: inherit;
39
- opacity: 0.5;
40
- cursor: pointer;
41
- outline: none;
42
- }
43
-
44
- /* ---- collapsed state: tick bars, width by heading level ---- */
45
-
46
- .jp-mm-bar {
47
- display: block;
48
- height: 2px;
49
- border-radius: 1px;
50
- background: currentColor;
51
- }
52
-
53
- .jp-mm-l1 .jp-mm-bar { width: 18px; }
54
- .jp-mm-l2 .jp-mm-bar { width: 13px; }
55
- .jp-mm-l3 .jp-mm-bar { width: 9px; }
56
- .jp-mm-l4 .jp-mm-bar { width: 7px; }
57
- .jp-mm-l5 .jp-mm-bar { width: 5px; }
58
- .jp-mm-l6 .jp-mm-bar { width: 5px; }
59
-
60
- .jp-mm-label { display: none; }
61
-
62
- /* ---- expanded state (hover) ---- */
63
-
64
- #jp-minimap:hover .jp-mm-list {
65
- align-items: stretch;
66
- overflow-y: auto;
67
- overscroll-behavior: contain;
68
- background: rgba(127, 127, 127, 0.16);
69
- backdrop-filter: blur(10px);
70
- -webkit-backdrop-filter: blur(10px);
71
- box-shadow: 0 6px 28px rgba(0, 0, 0, 0.28);
72
- }
73
-
74
- #jp-minimap:hover .jp-mm-bar { display: none; }
75
-
76
- #jp-minimap:hover .jp-mm-item { justify-content: flex-start; }
77
-
78
- #jp-minimap:hover .jp-mm-label {
79
- display: block;
80
- max-width: var(--jp-mm-width, 240px);
81
- white-space: nowrap;
82
- overflow: hidden;
83
- text-overflow: ellipsis;
84
- }
85
-
86
- /* indent by heading level when expanded */
87
- #jp-minimap:hover .jp-mm-l2 { padding-left: 16px; }
88
- #jp-minimap:hover .jp-mm-l3 { padding-left: 28px; }
89
- #jp-minimap:hover .jp-mm-l4 { padding-left: 40px; }
90
- #jp-minimap:hover .jp-mm-l5 { padding-left: 52px; }
91
- #jp-minimap:hover .jp-mm-l6 { padding-left: 52px; }
92
-
93
- /* ---- shared states ---- */
94
-
95
- .jp-mm-item:hover {
96
- opacity: 1;
97
- background: rgba(127, 127, 127, 0.22);
98
- }
99
-
100
- .jp-mm-active { opacity: 1; }
101
-
102
- #jp-minimap:hover .jp-mm-active {
103
- background: rgba(127, 127, 127, 0.18);
104
- }
105
-
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; }
111
-
112
- /* don't show over printed/exported output */
113
- @media print {
114
- #jp-minimap { display: none; }
115
- }