joplin-plugin-minimap 1.3.0 → 1.4.1
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 +22 -0
- package/package.json +1 -1
- package/publish/index.js +45 -0
- package/publish/manifest.json +1 -1
- package/publish/minimap-view.js +83 -5
- package/publish/plugin.jpl +0 -0
package/README.md
CHANGED
|
@@ -57,9 +57,31 @@ Configurable in **Tools → Options → Joplin Minimap**:
|
|
|
57
57
|
- **Minimap side** — which edge of the viewer the minimap docks to, right or left (default right)
|
|
58
58
|
- **Default depth** — deepest heading level shown when a note opens; the `+`/`-` buttons change it on the fly (default 6, every level)
|
|
59
59
|
- **Edge distance (px)** — gap between the minimap and the viewer edge it sits on (default 6)
|
|
60
|
+
- **Text size (%)** — scales the text in the expanded panel; raise it on high-DPI screens (default 100, under *Advanced*)
|
|
61
|
+
- **High contrast panel** — brighter text and a more solid panel background, using no fixed colours so it still follows your theme (default off, under *Advanced*)
|
|
60
62
|
|
|
61
63
|
Changed settings apply on the next render (switch notes or edit the note).
|
|
62
64
|
|
|
65
|
+
### Styling it yourself
|
|
66
|
+
|
|
67
|
+
Every colour and size the panel uses reads from a CSS variable, so you can restyle it from `userstyle.css` in your Joplin profile directory (it applies to the rendered note viewer, which is where the minimap lives). Set them on `:root` — no `!important` needed:
|
|
68
|
+
|
|
69
|
+
```css
|
|
70
|
+
:root {
|
|
71
|
+
--jp-mm-font: 15px; /* base text size */
|
|
72
|
+
--jp-mm-color: #e8e8e8; /* outline text */
|
|
73
|
+
--jp-mm-bg: rgba(30, 30, 30, 0.92); /* expanded panel background */
|
|
74
|
+
--jp-mm-dim: 0.85; /* opacity of inactive rows */
|
|
75
|
+
--jp-mm-dot: #cd6155; /* open-to-do dot */
|
|
76
|
+
--jp-mm-width: 320px; /* expanded panel max width */
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Joplin creates and opens the file for you: **Tools → Options → Appearance → Show Advanced Settings → Custom stylesheet for rendered Markdown**. It is `userstyle.css` in your profile directory (`%USERPROFILE%\.config\joplin-desktop\` on Windows, `~/.config/joplin-desktop/` elsewhere) if you would rather edit it directly. Restart Joplin afterwards.
|
|
81
|
+
|
|
82
|
+
Try **High contrast panel** first — it fixes most legibility complaints without picking any colours, so it keeps working when you switch themes.
|
|
83
|
+
|
|
84
|
+
|
|
63
85
|
## Development
|
|
64
86
|
|
|
65
87
|
Zero dependencies — no `npm install` needed:
|
package/package.json
CHANGED
package/publish/index.js
CHANGED
|
@@ -23,6 +23,10 @@ const SETTINGS_I18N = {
|
|
|
23
23
|
sideDesc: 'Which edge of the viewer the minimap sits on. Default: right.',
|
|
24
24
|
sideRight: 'Right',
|
|
25
25
|
sideLeft: 'Left',
|
|
26
|
+
fontScale: 'Text size (%)',
|
|
27
|
+
fontScaleDesc: 'Scales the text in the expanded panel. 100 keeps the current size; raise it on high-DPI screens or if the outline is hard to read. Default: 100.',
|
|
28
|
+
highContrast: 'High contrast panel',
|
|
29
|
+
highContrastDesc: 'Makes the outline text brighter and the panel background more solid, so it separates from the note. Uses no fixed colours, so it still follows your theme. Turn this on before reaching for custom colours. Default: off.',
|
|
26
30
|
maxLevel: 'Default depth',
|
|
27
31
|
maxLevelDesc: 'Deepest heading level shown when a note opens. The +/- buttons at the top of the expanded panel change it on the fly. Default: 6 (every level).',
|
|
28
32
|
showTodos: 'Show to-do markers',
|
|
@@ -39,6 +43,10 @@ const SETTINGS_I18N = {
|
|
|
39
43
|
sideDesc: '小地图停靠在阅读器的哪一侧边缘。默认靠右。',
|
|
40
44
|
sideRight: '右侧',
|
|
41
45
|
sideLeft: '左侧',
|
|
46
|
+
fontScale: '文字大小(%)',
|
|
47
|
+
fontScaleDesc: '缩放展开面板里的文字。100 为当前大小;高分屏或看不清时调大。默认 100。',
|
|
48
|
+
highContrast: '高对比度面板',
|
|
49
|
+
highContrastDesc: '提高目录文字的亮度、加实面板背景,使其与正文分开。不使用固定颜色,仍然跟随主题。想自定义颜色前先试这个。默认关闭。',
|
|
42
50
|
maxLevel: '默认层级深度',
|
|
43
51
|
maxLevelDesc: '打开笔记时显示到第几级标题。展开面板顶部的 +/- 按钮可随时调整。默认 6(全部层级)。',
|
|
44
52
|
showTodos: '显示待办标记',
|
|
@@ -55,6 +63,10 @@ const SETTINGS_I18N = {
|
|
|
55
63
|
sideDesc: '小地圖停靠在檢視器的哪一側邊緣。預設靠右。',
|
|
56
64
|
sideRight: '右側',
|
|
57
65
|
sideLeft: '左側',
|
|
66
|
+
fontScale: '文字大小(%)',
|
|
67
|
+
fontScaleDesc: '縮放展開面板裡的文字。100 為目前大小;高解析度螢幕或看不清時調大。預設 100。',
|
|
68
|
+
highContrast: '高對比面板',
|
|
69
|
+
highContrastDesc: '提高目錄文字亮度、加實面板背景,使其與內文分開。不使用固定顏色,仍然跟隨主題。想自訂顏色前先試這個。預設關閉。',
|
|
58
70
|
maxLevel: '預設層級深度',
|
|
59
71
|
maxLevelDesc: '開啟筆記時顯示到第幾級標題。展開面板頂部的 +/- 按鈕可隨時調整。預設 6(全部層級)。',
|
|
60
72
|
showTodos: '顯示待辦標記',
|
|
@@ -71,6 +83,10 @@ const SETTINGS_I18N = {
|
|
|
71
83
|
sideDesc: 'У какого края области просмотра располагается миникарта. По умолчанию: справа.',
|
|
72
84
|
sideRight: 'Справа',
|
|
73
85
|
sideLeft: 'Слева',
|
|
86
|
+
fontScale: 'Размер текста (%)',
|
|
87
|
+
fontScaleDesc: 'Масштаб текста в развёрнутой панели. 100 - текущий размер. По умолчанию: 100.',
|
|
88
|
+
highContrast: 'Контрастная панель',
|
|
89
|
+
highContrastDesc: 'Делает текст ярче, а фон панели плотнее, чтобы она отделялась от заметки. Без фиксированных цветов - тема сохраняется. По умолчанию: выкл.',
|
|
74
90
|
maxLevel: 'Глубина по умолчанию',
|
|
75
91
|
maxLevelDesc: 'До какого уровня заголовков показывать при открытии заметки. Кнопки +/- вверху развёрнутой панели меняют её на лету. По умолчанию: 6 (все уровни).',
|
|
76
92
|
showTodos: 'Показывать метки задач',
|
|
@@ -87,6 +103,10 @@ const SETTINGS_I18N = {
|
|
|
87
103
|
sideDesc: 'ミニマップをビューアーのどちら側の端に表示するか。既定値:右。',
|
|
88
104
|
sideRight: '右',
|
|
89
105
|
sideLeft: '左',
|
|
106
|
+
fontScale: '文字サイズ(%)',
|
|
107
|
+
fontScaleDesc: '展開パネル内の文字を拡大縮小します。100 が現在のサイズです。既定値:100。',
|
|
108
|
+
highContrast: '高コントラスト表示',
|
|
109
|
+
highContrastDesc: '見出しの文字を明るくし、パネル背景を濃くして本文と区別しやすくします。固定色は使わず、テーマに追従します。既定値:オフ。',
|
|
90
110
|
maxLevel: '既定の階層の深さ',
|
|
91
111
|
maxLevelDesc: 'ノートを開いたときに表示する見出しの深さ。展開パネル上部の +/- ボタンでいつでも変更できます。既定値:6(すべての階層)。',
|
|
92
112
|
showTodos: 'ToDoマーカーを表示',
|
|
@@ -170,6 +190,29 @@ joplin.plugins.register({
|
|
|
170
190
|
label: t.maxLevel,
|
|
171
191
|
description: t.maxLevelDesc,
|
|
172
192
|
},
|
|
193
|
+
// A percentage, not a px value: the base is 12.5px and Joplin's Int
|
|
194
|
+
// setting cannot hold that, so 100 maps to exactly today's size and
|
|
195
|
+
// nobody's panel changes on upgrade.
|
|
196
|
+
'minimapFontScale': {
|
|
197
|
+
value: 100,
|
|
198
|
+
minimum: 50,
|
|
199
|
+
maximum: 250,
|
|
200
|
+
type: TYPE_INT,
|
|
201
|
+
section: 'minimap',
|
|
202
|
+
public: true,
|
|
203
|
+
advanced: true,
|
|
204
|
+
label: t.fontScale,
|
|
205
|
+
description: t.fontScaleDesc,
|
|
206
|
+
},
|
|
207
|
+
'minimapHighContrast': {
|
|
208
|
+
value: false,
|
|
209
|
+
type: TYPE_BOOL,
|
|
210
|
+
section: 'minimap',
|
|
211
|
+
public: true,
|
|
212
|
+
advanced: true,
|
|
213
|
+
label: t.highContrast,
|
|
214
|
+
description: t.highContrastDesc,
|
|
215
|
+
},
|
|
173
216
|
'minimapShowTodos': {
|
|
174
217
|
value: true,
|
|
175
218
|
type: TYPE_BOOL,
|
|
@@ -197,6 +240,8 @@ joplin.plugins.register({
|
|
|
197
240
|
rightOffset: await joplin.settings.value('minimapRightOffset'),
|
|
198
241
|
side: await joplin.settings.value('minimapSide'),
|
|
199
242
|
maxLevel: await joplin.settings.value('minimapMaxLevel'),
|
|
243
|
+
fontScale: await joplin.settings.value('minimapFontScale'),
|
|
244
|
+
highContrast: await joplin.settings.value('minimapHighContrast'),
|
|
200
245
|
showTodos: await joplin.settings.value('minimapShowTodos'),
|
|
201
246
|
};
|
|
202
247
|
}
|
package/publish/manifest.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"desktop",
|
|
8
8
|
"mobile"
|
|
9
9
|
],
|
|
10
|
-
"version": "1.
|
|
10
|
+
"version": "1.4.1",
|
|
11
11
|
"name": "Joplin Minimap",
|
|
12
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.",
|
|
13
13
|
"author": "lim0513",
|
package/publish/minimap-view.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
// Defaults; overridden from Joplin's plugin settings (Tools > Options > Minimap)
|
|
10
10
|
// when webviewApi is available in this webview.
|
|
11
|
-
var settings = { minHeadings: 2, panelWidth: 240, rightOffset: 6, side: 'right', maxLevel: 6, showTodos: true };
|
|
11
|
+
var settings = { minHeadings: 2, panelWidth: 240, rightOffset: 6, side: 'right', maxLevel: 6, fontScale: 100, highContrast: false, showTodos: true };
|
|
12
12
|
|
|
13
13
|
function loadSettings() {
|
|
14
14
|
if (typeof webviewApi === 'undefined' || !webviewApi.postMessage) {
|
|
@@ -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.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:
|
|
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: var(--jp-mm-font, 12.5px);\n\tline-height: 1.35;\n\tcolor: var(--jp-mm-color, 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: var(--jp-mm-dim, 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: var(--jp-mm-bg, 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: 0.88em;\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: 1.08em; 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: 0.92em; 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/* ---- high contrast (issue #5) ---- */\n\n/* Still routed through the same variables, so a userstyle.css override wins\n * over the mode rather than fighting it. */\n#jp-minimap.jp-mm-hc .jp-mm-item { opacity: var(--jp-mm-dim, 0.82); }\n#jp-minimap.jp-mm-hc.jp-mm-open .jp-mm-r2 .jp-mm-label { opacity: 0.95; }\n\n#jp-minimap.jp-mm-hc.jp-mm-open .jp-mm-list {\n\tbackground: var(--jp-mm-bg, rgba(127, 127, 127, 0.38));\n\tbox-shadow: 0 6px 28px rgba(0, 0, 0, 0.45);\n}\n\n#jp-minimap.jp-mm-hc.jp-mm-open .jp-mm-head { opacity: 1; }\n#jp-minimap.jp-mm-hc .jp-mm-step { background: rgba(127, 127, 127, 0.4); }\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: var(--jp-mm-dot, 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: 1.12em; }\n\t#jp-minimap.jp-mm-open .jp-mm-r0 .jp-mm-label { font-size: 1.2em; }\n\t#jp-minimap.jp-mm-open .jp-mm-r2 .jp-mm-label { font-size: 1.04em; }\n\n\t#jp-minimap.jp-mm-open .jp-mm-head { font-size: 1.04em; 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;
|
|
@@ -116,7 +116,7 @@
|
|
|
116
116
|
return Array.prototype.slice.call(root.querySelectorAll('h1, h2, h3, h4, h5, h6'));
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
function
|
|
119
|
+
function findHeading(index, text) {
|
|
120
120
|
var hs = liveHeadings();
|
|
121
121
|
var h = hs[index];
|
|
122
122
|
if (!h || (text && (h.textContent || '').trim() !== text)) {
|
|
@@ -124,7 +124,74 @@
|
|
|
124
124
|
if ((hs[i].textContent || '').trim() === text) { h = hs[i]; break; }
|
|
125
125
|
}
|
|
126
126
|
}
|
|
127
|
-
|
|
127
|
+
return h;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// A smooth scrollIntoView is fire-and-forget: the browser animates towards
|
|
131
|
+
// an offset worked out when it STARTS, and plenty can cut it short or make
|
|
132
|
+
// it land wrong - the reader nudging the wheel, Joplin re-rendering the note
|
|
133
|
+
// underneath, split view writing scrollTop of its own, or images above the
|
|
134
|
+
// target finishing layout and shifting it. The jump then stops somewhere in
|
|
135
|
+
// between, which is the "clicked H1 but only went up to 4.5" report.
|
|
136
|
+
// Some environments ignore behavior:'smooth' altogether (headless Chromium
|
|
137
|
+
// does, measured) and the jump never moves at all.
|
|
138
|
+
//
|
|
139
|
+
// So the animation is started for the feel, then the heading is watched
|
|
140
|
+
// until it stops moving and an INSTANT scroll closes whatever gap is left.
|
|
141
|
+
// The instant call uses the same alignment, so when the smooth scroll did
|
|
142
|
+
// land correctly it is a no-op.
|
|
143
|
+
var jumpWatch = null;
|
|
144
|
+
var jumpRelease = null;
|
|
145
|
+
|
|
146
|
+
function stopJumpWatch() {
|
|
147
|
+
if (jumpWatch) { clearInterval(jumpWatch); jumpWatch = null; }
|
|
148
|
+
if (jumpRelease) { jumpRelease(); jumpRelease = null; }
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function watchJump(index, text) {
|
|
152
|
+
stopJumpWatch();
|
|
153
|
+
|
|
154
|
+
// The reader always wins: if they scroll themselves we are no longer
|
|
155
|
+
// correcting a broken jump, we are fighting them.
|
|
156
|
+
var takenOver = false;
|
|
157
|
+
function onUserScroll(e) {
|
|
158
|
+
// Wheeling the ToC list itself is not the reader taking over the note -
|
|
159
|
+
// the panel has its own wheel handler, and clicking a row then scrolling
|
|
160
|
+
// the list to pick another is a normal sequence.
|
|
161
|
+
var t = e && e.target;
|
|
162
|
+
if (t && t.closest && t.closest('#jp-minimap')) return;
|
|
163
|
+
takenOver = true;
|
|
164
|
+
}
|
|
165
|
+
document.addEventListener('wheel', onUserScroll, { passive: true, capture: true });
|
|
166
|
+
document.addEventListener('touchstart', onUserScroll, { passive: true, capture: true });
|
|
167
|
+
jumpRelease = function () {
|
|
168
|
+
document.removeEventListener('wheel', onUserScroll, { capture: true });
|
|
169
|
+
document.removeEventListener('touchstart', onUserScroll, { capture: true });
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
var ticks = 0;
|
|
173
|
+
var prevTop = null;
|
|
174
|
+
jumpWatch = setInterval(function () {
|
|
175
|
+
ticks++;
|
|
176
|
+
if (takenOver) { stopJumpWatch(); return; }
|
|
177
|
+
var h = findHeading(index, text);
|
|
178
|
+
// A rebuild can briefly detach it; keep waiting, within reason.
|
|
179
|
+
if (!h || !h.isConnected) { if (ticks > 20) stopJumpWatch(); return; }
|
|
180
|
+
var top = Math.round(h.getBoundingClientRect().top);
|
|
181
|
+
var settled = prevTop !== null && top === prevTop;
|
|
182
|
+
prevTop = top;
|
|
183
|
+
if (settled || ticks > 20) {
|
|
184
|
+
stopJumpWatch();
|
|
185
|
+
h.scrollIntoView({ block: 'start' });
|
|
186
|
+
}
|
|
187
|
+
}, 70);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function jumpTo(index, text) {
|
|
191
|
+
var h = findHeading(index, text);
|
|
192
|
+
if (!h || !h.isConnected) return;
|
|
193
|
+
h.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
194
|
+
watchJump(index, text);
|
|
128
195
|
}
|
|
129
196
|
|
|
130
197
|
// Width of the note viewer's right-edge scrollbar zone. Overlay scrollbars
|
|
@@ -200,8 +267,10 @@
|
|
|
200
267
|
// left and right set stretches between them instead of hugging one edge.
|
|
201
268
|
// The scrollbar gap is a right-edge concern only - the viewer's scrollbar
|
|
202
269
|
// never sits on the left, so no gap is added there.
|
|
270
|
+
var navClasses = [];
|
|
271
|
+
if (settings.highContrast === true) navClasses.push('jp-mm-hc');
|
|
203
272
|
if (settings.side === 'left') {
|
|
204
|
-
|
|
273
|
+
navClasses.push('jp-mm-left');
|
|
205
274
|
nav.style.left = settings.rightOffset + 'px';
|
|
206
275
|
nav.style.right = 'auto';
|
|
207
276
|
} else {
|
|
@@ -211,7 +280,16 @@
|
|
|
211
280
|
nav.style.right = (settings.rightOffset + (isTouch ? 0 : scrollbarGap(root))) + 'px';
|
|
212
281
|
nav.style.left = 'auto';
|
|
213
282
|
}
|
|
283
|
+
nav.className = navClasses.join(' ');
|
|
214
284
|
nav.style.setProperty('--jp-mm-width', settings.panelWidth + 'px');
|
|
285
|
+
// Only set when it differs from the default. An inline custom property
|
|
286
|
+
// beats any stylesheet rule, so writing it unconditionally would make
|
|
287
|
+
// --jp-mm-font impossible to override from userstyle.css without
|
|
288
|
+
// !important - and 100% is exactly the stylesheet's own fallback anyway.
|
|
289
|
+
var scale = Number(settings.fontScale) || 100;
|
|
290
|
+
if (scale !== 100) {
|
|
291
|
+
nav.style.setProperty('--jp-mm-font', (12.5 * scale / 100).toFixed(2) + 'px');
|
|
292
|
+
}
|
|
215
293
|
// The viewer DOM can be editable in some contexts; make sure the
|
|
216
294
|
// minimap never shows a caret or accepts keyboard input.
|
|
217
295
|
// NOTE: no preventDefault on mousedown! Blocking the default mouse-focus
|
package/publish/plugin.jpl
CHANGED
|
Binary file
|