joplin-plugin-minimap 1.2.0 → 1.3.0

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/README.md CHANGED
@@ -7,6 +7,7 @@ A hover-to-expand table of contents minimap for the [Joplin](https://joplinapp.o
7
7
  ## Features
8
8
 
9
9
  - **Tick Bar Minimap** — Each heading is shown as a small horizontal bar on the right edge of the note viewer; bar length reflects the heading level (H1 longest)
10
+ - **Desktop and Mobile** — Hover on desktop, tap on touch devices: the collapsed strip is a single tap target, rows and buttons grow to finger size when open, and tapping outside or picking a heading closes it again
10
11
  - **Hover to Expand** — Move the mouse over the bars and they expand into a full table of contents overlay, indented by level, with long titles ellipsized
11
12
  - **Depth Stepper** — `+` / `-` buttons at the top of the expanded panel raise or lower how deep the outline goes, so a long nested note collapses to its top-level structure in one click
12
13
  - **Heading Tiers** — Top-level headings render larger and heavier than nested ones, so the shape of the document is readable at a glance
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "joplin-plugin-minimap",
3
- "version": "1.2.0",
4
- "description": "A hover-to-expand table of contents minimap for the Joplin note viewer",
3
+ "version": "1.3.0",
4
+ "description": "A table of contents minimap for the Joplin note viewer, on desktop and mobile",
5
5
  "author": "lim0513",
6
6
  "homepage": "https://github.com/lim0513/joplin-minimap",
7
7
  "repository": {
@@ -1,10 +1,15 @@
1
1
  {
2
2
  "manifest_version": 1,
3
3
  "id": "com.github.joplin-minimap",
4
- "app_min_version": "2.8",
5
- "version": "1.2.0",
4
+ "app_min_version": "2.8",
5
+ "app_min_version_mobile": "3.2",
6
+ "platforms": [
7
+ "desktop",
8
+ "mobile"
9
+ ],
10
+ "version": "1.3.0",
6
11
  "name": "Joplin Minimap",
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.",
12
+ "description": "A table of contents minimap for the note viewer. Shows heading tick marks along one edge; hover (or tap on mobile) to expand into a full outline, click to jump.",
8
13
  "author": "lim0513",
9
14
  "homepage_url": "https://github.com/lim0513/joplin-minimap",
10
15
  "repository_url": "https://github.com/lim0513/joplin-minimap",
@@ -47,7 +47,7 @@
47
47
  // then renders UNSTYLED as flow content below the note (looks like a
48
48
  // duplicated outline under the document). Keeping the CSS inline and
49
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\t/* Collapsed geometry is direction-independent: the tick bars always\n\t * hug the docked edge, whichever side that is, even in an RTL note. Per-item direction applies\n\t * to the expanded panel only (see the dir=rtl rule below). */\n\tdirection: ltr;\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\tdirection: ltr;\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/* ---- depth stepper (issue #2) ---- */\n\n/* Rows deeper than the current depth are HIDDEN, not removed: keeping the\n * nav element alive is what lets the panel stay open across a depth change,\n * since the whole expansion rides on :hover over #jp-minimap. */\n#jp-minimap[data-depth=\"1\"] .jp-mm-l2,\n#jp-minimap[data-depth=\"1\"] .jp-mm-l3,\n#jp-minimap[data-depth=\"1\"] .jp-mm-l4,\n#jp-minimap[data-depth=\"1\"] .jp-mm-l5,\n#jp-minimap[data-depth=\"1\"] .jp-mm-l6,\n#jp-minimap[data-depth=\"2\"] .jp-mm-l3,\n#jp-minimap[data-depth=\"2\"] .jp-mm-l4,\n#jp-minimap[data-depth=\"2\"] .jp-mm-l5,\n#jp-minimap[data-depth=\"2\"] .jp-mm-l6,\n#jp-minimap[data-depth=\"3\"] .jp-mm-l4,\n#jp-minimap[data-depth=\"3\"] .jp-mm-l5,\n#jp-minimap[data-depth=\"3\"] .jp-mm-l6,\n#jp-minimap[data-depth=\"4\"] .jp-mm-l5,\n#jp-minimap[data-depth=\"4\"] .jp-mm-l6,\n#jp-minimap[data-depth=\"5\"] .jp-mm-l6 { display: none; }\n\n/* The stepper is part of the expanded panel only - collapsed, the minimap\n * stays the same narrow strip of tick marks it has always been. */\n.jp-mm-head { display: none; }\n\n\n/* ---- docked on the left edge ---- */\n\n/* Mirror the collapsed alignment so the tick bars hug the left border.\n * direction is pinned to ltr above, so flex-start is unambiguously the left\n * side whatever language the note is in. Placed BEFORE the :hover rules on\n * purpose: they carry the same specificity, so source order decides. */\n#jp-minimap.jp-mm-left .jp-mm-list { align-items: flex-start; }\n#jp-minimap.jp-mm-left .jp-mm-item { justify-content: flex-start; }\n\n/* The to-do dot mirrors too: docked right it hangs left of the tick bar,\n * docked left it must hang RIGHT of it - otherwise a dotted row pushes its\n * bar 9px inward (4px dot + 5px gap) and the flush bar column goes ragged.\n * order= reorders the flex row without touching the DOM, so the expanded\n * panel can put the dot back in front of the label. */\n#jp-minimap.jp-mm-left .jp-mm-dot {\n\torder: 1;\n\tmargin-inline-start: 5px;\n\tmargin-inline-end: 0;\n}\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\ttext-align: start;\n}\n\n/* RTL headings (Persian/Arabic/Hebrew) read right-to-left in the expanded\n * panel: justify-content, padding-inline-start and text-align:start all flip\n * with the row direction, so the label hugs the right edge and nested levels\n * indent inward from the right. build() sets dir per row (first strong char). */\n#jp-minimap:hover .jp-mm-item[dir=\"rtl\"] { direction: rtl; }\n\n/* indent by heading level when expanded (logical: left in LTR, right in RTL) */\n#jp-minimap:hover .jp-mm-l2 { padding-inline-start: 16px; }\n#jp-minimap:hover .jp-mm-l3 { padding-inline-start: 28px; }\n#jp-minimap:hover .jp-mm-l4 { padding-inline-start: 40px; }\n#jp-minimap:hover .jp-mm-l5 { padding-inline-start: 52px; }\n#jp-minimap:hover .jp-mm-l6 { padding-inline-start: 52px; }\n\n#jp-minimap:hover .jp-mm-head {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tgap: 7px;\n\tpadding: 0 4px 5px;\n\tmargin-bottom: 4px;\n\tborder-bottom: 1px solid rgba(127, 127, 127, 0.3);\n\tfont-size: 11px;\n\topacity: 0.8;\n\t/* The control reads the same either way round, and the panel is pinned\n\t * to ltr anyway - keep it out of the per-row bidi logic entirely. */\n\tdirection: ltr;\n}\n\n.jp-mm-step {\n\tmin-width: 15px;\n\ttext-align: center;\n\tborder-radius: 4px;\n\tfont-weight: 700;\n\tcursor: pointer;\n\tbackground: rgba(127, 127, 127, 0.22);\n}\n\n#jp-minimap:hover .jp-mm-step:hover { background: rgba(127, 127, 127, 0.45); }\n\n/* Already at the shallowest / deepest level this note has. */\n.jp-mm-step-off { opacity: 0.3; cursor: default; }\n#jp-minimap:hover .jp-mm-step-off:hover { background: rgba(127, 127, 127, 0.22); }\n\n.jp-mm-depth { font-variant-numeric: tabular-nums; }\n\n/* ---- heading tiers (issue #2): indentation alone is a weak cue when\n * scanning a long outline. Size and weight only - NEVER a colour. The\n * panel has to stay legible on every Joplin theme, so the palette is\n * limited to currentColor and neutral rgba (see CLAUDE.md). ---- */\n#jp-minimap:hover .jp-mm-r0 .jp-mm-label { font-size: 13.5px; font-weight: 600; }\n#jp-minimap:hover .jp-mm-r1 .jp-mm-label { font-weight: 500; }\n#jp-minimap:hover .jp-mm-r2 .jp-mm-label { font-size: 11.5px; opacity: 0.8; }\n\n\n/* Expanded, the dot reads as a marker BEFORE the title on either edge. */\n#jp-minimap.jp-mm-left:hover .jp-mm-dot {\n\torder: 0;\n\tmargin-inline-start: 0;\n\tmargin-inline-end: 5px;\n}\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/* ---- open-to-do section dot: one muted red dot before the tick bar of\n * any section that contains at least one unchecked checkbox ---- */\n.jp-mm-dot {\n\tdisplay: none;\n\twidth: 4px;\n\theight: 4px;\n\tborder-radius: 50%;\n\tbackground: rgba(205, 97, 85, 0.85);\n\tmargin-inline-end: 5px;\n\tflex: none;\n}\n\n.jp-mm-dot.jp-mm-on { display: block; }\n\n/* don't show over printed/exported output */\n@media print {\n\t#jp-minimap { display: none; }\n}\n";
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\t/* Collapsed geometry is direction-independent: the tick bars always\n\t * hug the docked edge, whichever side that is, even in an RTL note. Per-item direction applies\n\t * to the expanded panel only (see the dir=rtl rule below). */\n\tdirection: ltr;\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\tdirection: ltr;\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/* ---- depth stepper (issue #2) ---- */\n\n/* Rows deeper than the current depth are HIDDEN, not removed: keeping the\n * nav element alive is what lets the panel stay open across a depth change,\n * since the whole expansion rides on :hover over #jp-minimap. */\n#jp-minimap[data-depth=\"1\"] .jp-mm-l2,\n#jp-minimap[data-depth=\"1\"] .jp-mm-l3,\n#jp-minimap[data-depth=\"1\"] .jp-mm-l4,\n#jp-minimap[data-depth=\"1\"] .jp-mm-l5,\n#jp-minimap[data-depth=\"1\"] .jp-mm-l6,\n#jp-minimap[data-depth=\"2\"] .jp-mm-l3,\n#jp-minimap[data-depth=\"2\"] .jp-mm-l4,\n#jp-minimap[data-depth=\"2\"] .jp-mm-l5,\n#jp-minimap[data-depth=\"2\"] .jp-mm-l6,\n#jp-minimap[data-depth=\"3\"] .jp-mm-l4,\n#jp-minimap[data-depth=\"3\"] .jp-mm-l5,\n#jp-minimap[data-depth=\"3\"] .jp-mm-l6,\n#jp-minimap[data-depth=\"4\"] .jp-mm-l5,\n#jp-minimap[data-depth=\"4\"] .jp-mm-l6,\n#jp-minimap[data-depth=\"5\"] .jp-mm-l6 { display: none; }\n\n/* The stepper is part of the expanded panel only - collapsed, the minimap\n * stays the same narrow strip of tick marks it has always been. */\n.jp-mm-head { display: none; }\n\n\n/* ---- docked on the left edge ---- */\n\n/* Mirror the collapsed alignment so the tick bars hug the left border.\n * direction is pinned to ltr above, so flex-start is unambiguously the left\n * side whatever language the note is in. Placed BEFORE the :hover rules on\n * purpose: they carry the same specificity, so source order decides. */\n#jp-minimap.jp-mm-left .jp-mm-list { align-items: flex-start; }\n#jp-minimap.jp-mm-left .jp-mm-item { justify-content: flex-start; }\n\n/* The to-do dot mirrors too: docked right it hangs left of the tick bar,\n * docked left it must hang RIGHT of it - otherwise a dotted row pushes its\n * bar 9px inward (4px dot + 5px gap) and the flush bar column goes ragged.\n * order= reorders the flex row without touching the DOM, so the expanded\n * panel can put the dot back in front of the label. */\n#jp-minimap.jp-mm-left .jp-mm-dot {\n\torder: 1;\n\tmargin-inline-start: 5px;\n\tmargin-inline-end: 0;\n}\n\n/* ---- expanded state (hover) ---- */\n\n#jp-minimap.jp-mm-open .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.jp-mm-open .jp-mm-bar { display: none; }\n\n#jp-minimap.jp-mm-open .jp-mm-item { justify-content: flex-start; }\n\n#jp-minimap.jp-mm-open .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\ttext-align: start;\n}\n\n/* RTL headings (Persian/Arabic/Hebrew) read right-to-left in the expanded\n * panel: justify-content, padding-inline-start and text-align:start all flip\n * with the row direction, so the label hugs the right edge and nested levels\n * indent inward from the right. build() sets dir per row (first strong char). */\n#jp-minimap.jp-mm-open .jp-mm-item[dir=\"rtl\"] { direction: rtl; }\n\n/* indent by heading level when expanded (logical: left in LTR, right in RTL) */\n#jp-minimap.jp-mm-open .jp-mm-l2 { padding-inline-start: 16px; }\n#jp-minimap.jp-mm-open .jp-mm-l3 { padding-inline-start: 28px; }\n#jp-minimap.jp-mm-open .jp-mm-l4 { padding-inline-start: 40px; }\n#jp-minimap.jp-mm-open .jp-mm-l5 { padding-inline-start: 52px; }\n#jp-minimap.jp-mm-open .jp-mm-l6 { padding-inline-start: 52px; }\n\n#jp-minimap.jp-mm-open .jp-mm-head {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tgap: 7px;\n\tpadding: 0 4px 5px;\n\tmargin-bottom: 4px;\n\tborder-bottom: 1px solid rgba(127, 127, 127, 0.3);\n\tfont-size: 11px;\n\topacity: 0.8;\n\t/* The control reads the same either way round, and the panel is pinned\n\t * to ltr anyway - keep it out of the per-row bidi logic entirely. */\n\tdirection: ltr;\n}\n\n.jp-mm-step {\n\tmin-width: 15px;\n\ttext-align: center;\n\tborder-radius: 4px;\n\tfont-weight: 700;\n\tcursor: pointer;\n\tbackground: rgba(127, 127, 127, 0.22);\n}\n\n#jp-minimap.jp-mm-open .jp-mm-step:hover { background: rgba(127, 127, 127, 0.45); }\n\n/* Already at the shallowest / deepest level this note has. */\n.jp-mm-step-off { opacity: 0.3; cursor: default; }\n#jp-minimap.jp-mm-open .jp-mm-step-off:hover { background: rgba(127, 127, 127, 0.22); }\n\n.jp-mm-depth { font-variant-numeric: tabular-nums; }\n\n/* ---- heading tiers (issue #2): indentation alone is a weak cue when\n * scanning a long outline. Size and weight only - NEVER a colour. The\n * panel has to stay legible on every Joplin theme, so the palette is\n * limited to currentColor and neutral rgba (see CLAUDE.md). ---- */\n#jp-minimap.jp-mm-open .jp-mm-r0 .jp-mm-label { font-size: 13.5px; font-weight: 600; }\n#jp-minimap.jp-mm-open .jp-mm-r1 .jp-mm-label { font-weight: 500; }\n#jp-minimap.jp-mm-open .jp-mm-r2 .jp-mm-label { font-size: 11.5px; opacity: 0.8; }\n\n\n/* Expanded, the dot reads as a marker BEFORE the title on either edge. */\n#jp-minimap.jp-mm-open.jp-mm-left .jp-mm-dot {\n\torder: 0;\n\tmargin-inline-start: 0;\n\tmargin-inline-end: 5px;\n}\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.jp-mm-open .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/* ---- open-to-do section dot: one muted red dot before the tick bar of\n * any section that contains at least one unchecked checkbox ---- */\n.jp-mm-dot {\n\tdisplay: none;\n\twidth: 4px;\n\theight: 4px;\n\tborder-radius: 50%;\n\tbackground: rgba(205, 97, 85, 0.85);\n\tmargin-inline-end: 5px;\n\tflex: none;\n}\n\n.jp-mm-dot.jp-mm-on { display: block; }\n\n/* don't show over printed/exported output */\n/* ---- touch devices ---- */\n\n/* (hover: none) is the device capability, not a screen width: a tablet with\n * a mouse keeps the desktop sizes, a touchscreen laptop gets these. */\n@media (hover: none) {\n\t/* Collapsed, the whole strip is the tap target - give it room. */\n\t.jp-mm-list { padding: 12px 14px; }\n\t.jp-mm-bar { height: 3px; }\n\n\t/* Expanded, each row has to be hittable on its own. */\n\t#jp-minimap.jp-mm-open .jp-mm-item { padding-top: 12px; padding-bottom: 12px; }\n\t#jp-minimap.jp-mm-open .jp-mm-label { font-size: 14px; }\n\t#jp-minimap.jp-mm-open .jp-mm-r0 .jp-mm-label { font-size: 15px; }\n\t#jp-minimap.jp-mm-open .jp-mm-r2 .jp-mm-label { font-size: 13px; }\n\n\t#jp-minimap.jp-mm-open .jp-mm-head { font-size: 13px; gap: 10px; padding-bottom: 8px; }\n\t.jp-mm-step { min-width: 32px; padding: 5px 0; }\n}\n@media print {\n\t#jp-minimap { display: none; }\n}\n";
51
51
 
52
52
  function ensureStyle() {
53
53
  if (document.getElementById('jp-minimap-style')) return;
@@ -95,6 +95,12 @@
95
95
  // note render, so anything held there is gone on the next keystroke.
96
96
  var userDepth = null;
97
97
 
98
+ // Touch or mouse. This is a device CAPABILITY question, not a screen-size
99
+ // one - a tablet with a mouse should behave like the desktop. The expanded
100
+ // panel is driven by a class rather than :hover precisely because of this:
101
+ // on touch, :hover latches after a tap and the panel gets stuck open.
102
+ var isTouch = !(window.matchMedia && window.matchMedia('(hover: hover)').matches);
103
+
98
104
  // Snap a wanted depth onto a level this note actually has (notes routinely
99
105
  // start at H2 or skip a level).
100
106
  function clampDepth(levels, want) {
@@ -199,7 +205,10 @@
199
205
  nav.style.left = settings.rightOffset + 'px';
200
206
  nav.style.right = 'auto';
201
207
  } else {
202
- nav.style.right = (settings.rightOffset + scrollbarGap(root)) + 'px';
208
+ // No scrollbar zone to dodge on touch: scrollbarGap() falls back to a
209
+ // 14px safety margin when it cannot measure one, which on a phone is
210
+ // just 14px of wasted inset.
211
+ nav.style.right = (settings.rightOffset + (isTouch ? 0 : scrollbarGap(root))) + 'px';
203
212
  nav.style.left = 'auto';
204
213
  }
205
214
  nav.style.setProperty('--jp-mm-width', settings.panelWidth + 'px');
@@ -219,6 +228,44 @@
219
228
  list.scrollTop += e.deltaY;
220
229
  }, { passive: false });
221
230
 
231
+ function setOpen(open) {
232
+ nav.classList.toggle('jp-mm-open', open);
233
+ // Drop the stepper's anchoring so the collapsed strip re-centres.
234
+ if (!open) { nav.style.top = ''; nav.style.transform = ''; }
235
+ }
236
+
237
+ var closeOnOutside = null;
238
+ if (isTouch) {
239
+ // Collapsed, the whole strip is one tap target: an individual tick bar
240
+ // is 18x2px, which no finger can hit. Once open, the rows take over.
241
+ var touchAt = null;
242
+ nav.addEventListener('touchstart', function (e) {
243
+ var t = e.touches[0];
244
+ touchAt = t ? { x: t.clientX, y: t.clientY, at: Date.now() } : null;
245
+ }, { passive: true });
246
+ nav.addEventListener('touchend', function (e) {
247
+ if (nav.classList.contains('jp-mm-open')) return;
248
+ var t = e.changedTouches[0];
249
+ // A drag is the reader scrolling the note, not tapping the minimap.
250
+ if (!touchAt || !t) return;
251
+ if (Date.now() - touchAt.at > 700) return;
252
+ if (Math.abs(t.clientX - touchAt.x) > 10 || Math.abs(t.clientY - touchAt.y) > 10) return;
253
+ // Also suppresses the synthetic mousedown, so the tap that OPENS the
254
+ // panel cannot fall through to whatever row lands under the finger.
255
+ e.preventDefault();
256
+ setOpen(true);
257
+ });
258
+ // touchstart, not click: it beats the browser's synthetic-click delay,
259
+ // so the panel is already shut by the time the tap lands on the note.
260
+ closeOnOutside = function (e) {
261
+ if (!nav.contains(e.target)) setOpen(false);
262
+ };
263
+ document.addEventListener('touchstart', closeOnOutside, { passive: true });
264
+ } else {
265
+ nav.addEventListener('mouseenter', function () { setOpen(true); });
266
+ nav.addEventListener('mouseleave', function () { setOpen(false); });
267
+ }
268
+
222
269
  var list = document.createElement('div');
223
270
  list.className = 'jp-mm-list';
224
271
 
@@ -342,6 +389,9 @@
342
389
  // re-apply this jump against the fresh DOM.
343
390
  pendingJump = { index: index, text: text, until: Date.now() + 1200 };
344
391
  jumpTo(index, text);
392
+ // On touch there is no cursor to move away, so the panel would sit
393
+ // over the heading the reader just asked to see.
394
+ if (isTouch) setOpen(false);
345
395
  });
346
396
 
347
397
  list.appendChild(item);
@@ -398,6 +448,7 @@
398
448
 
399
449
  cleanup = function () {
400
450
  document.removeEventListener('scroll', updateActive, { capture: true });
451
+ if (closeOnOutside) document.removeEventListener('touchstart', closeOnOutside);
401
452
  };
402
453
  }
403
454
 
Binary file