joplin-plugin-explorer 1.1.1 → 1.1.2
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/index.js +25 -6
- package/publish/manifest.json +11 -3
- package/publish/plugin.jpl +0 -0
- package/publish/webview/panel.css +70 -0
- package/publish/webview/panel.js +65 -8
package/package.json
CHANGED
package/publish/index.js
CHANGED
|
@@ -25,6 +25,7 @@ var i18nData = {
|
|
|
25
25
|
confirmDeleteNote: '确定删除此笔记吗?',
|
|
26
26
|
promptRename: '请输入新名称:',
|
|
27
27
|
promptMoveNote: '请输入目标笔记本名称:',
|
|
28
|
+
cancel: '取消',
|
|
28
29
|
},
|
|
29
30
|
'zh_TW': {
|
|
30
31
|
newNotebook: '新建筆記本', newNote: '新建筆記', newTodo: '新建待辦',
|
|
@@ -45,6 +46,7 @@ var i18nData = {
|
|
|
45
46
|
confirmDeleteFolder: '確定刪除此筆記本及其所有內容嗎?',
|
|
46
47
|
confirmDeleteNote: '確定刪除此筆記嗎?',
|
|
47
48
|
promptRename: '請輸入新名稱:', promptMoveNote: '請輸入目標筆記本名稱:',
|
|
49
|
+
cancel: '取消',
|
|
48
50
|
},
|
|
49
51
|
'en_US': {
|
|
50
52
|
newNotebook: 'New Notebook', newNote: 'New Note', newTodo: 'New To-do',
|
|
@@ -65,6 +67,7 @@ var i18nData = {
|
|
|
65
67
|
confirmDeleteFolder: 'Delete this notebook and all its contents?',
|
|
66
68
|
confirmDeleteNote: 'Delete this note?',
|
|
67
69
|
promptRename: 'Enter new name:', promptMoveNote: 'Enter target notebook name:',
|
|
70
|
+
cancel: 'Cancel',
|
|
68
71
|
},
|
|
69
72
|
'ja_JP': {
|
|
70
73
|
newNotebook: '\u65B0\u898F\u30CE\u30FC\u30C8\u30D6\u30C3\u30AF', newNote: '\u65B0\u898F\u30CE\u30FC\u30C8', newTodo: '\u65B0\u898F\u30BF\u30B9\u30AF',
|
|
@@ -85,6 +88,7 @@ var i18nData = {
|
|
|
85
88
|
confirmDeleteFolder: '\u3053\u306E\u30CE\u30FC\u30C8\u30D6\u30C3\u30AF\u3068\u305D\u306E\u5185\u5BB9\u3092\u3059\u3079\u3066\u524A\u9664\u3057\u307E\u3059\u304B\uFF1F',
|
|
86
89
|
confirmDeleteNote: '\u3053\u306E\u30CE\u30FC\u30C8\u3092\u524A\u9664\u3057\u307E\u3059\u304B\uFF1F',
|
|
87
90
|
promptRename: '\u65B0\u3057\u3044\u540D\u524D\u3092\u5165\u529B\uFF1A', promptMoveNote: '\u79FB\u52D5\u5148\u306E\u30CE\u30FC\u30C8\u30D6\u30C3\u30AF\u540D\uFF1A',
|
|
91
|
+
cancel: '\u30AD\u30E3\u30F3\u30BB\u30EB',
|
|
88
92
|
},
|
|
89
93
|
};
|
|
90
94
|
|
|
@@ -447,10 +451,14 @@ joplin.plugins.register({
|
|
|
447
451
|
if (itemType === 'folder') {
|
|
448
452
|
switch (action) {
|
|
449
453
|
case 'newNote':
|
|
450
|
-
await joplin.data.post(['
|
|
454
|
+
var newNote = await joplin.data.post(['notes'], null, { title: t.newNote, parent_id: id });
|
|
455
|
+
await joplin.commands.execute('openNote', newNote.id);
|
|
456
|
+
selectedNoteId = newNote.id;
|
|
451
457
|
break;
|
|
452
458
|
case 'newTodo':
|
|
453
|
-
await joplin.data.post(['
|
|
459
|
+
var newTodo = await joplin.data.post(['notes'], null, { title: t.newTodo, parent_id: id, is_todo: 1 });
|
|
460
|
+
await joplin.commands.execute('openNote', newTodo.id);
|
|
461
|
+
selectedNoteId = newTodo.id;
|
|
454
462
|
break;
|
|
455
463
|
case 'newSubNotebook':
|
|
456
464
|
await joplin.data.post(['folders'], null, { title: t.newNotebook, parent_id: id });
|
|
@@ -462,28 +470,39 @@ joplin.plugins.register({
|
|
|
462
470
|
if (msg.newTitle) await joplin.data.put(['folders', id], null, { title: msg.newTitle });
|
|
463
471
|
break;
|
|
464
472
|
case 'exportFolder':
|
|
465
|
-
await joplin.commands.execute('exportFolders', [id]);
|
|
473
|
+
try { await joplin.commands.execute('exportFolders', [id]); } catch(e) {}
|
|
466
474
|
break;
|
|
467
475
|
}
|
|
468
476
|
} else if (itemType === 'note') {
|
|
469
477
|
switch (action) {
|
|
470
478
|
case 'openNote':
|
|
471
479
|
await joplin.commands.execute('openNote', id);
|
|
480
|
+
selectedNoteId = id;
|
|
472
481
|
break;
|
|
473
482
|
case 'openInNewWindow':
|
|
474
|
-
await joplin.commands.execute('openNoteInNewWindow', id);
|
|
483
|
+
try { await joplin.commands.execute('openNoteInNewWindow', id); } catch(e) {
|
|
484
|
+
// Fallback: just open in main window
|
|
485
|
+
await joplin.commands.execute('openNote', id);
|
|
486
|
+
}
|
|
475
487
|
break;
|
|
476
488
|
case 'copyLink':
|
|
477
489
|
var linkNote = await joplin.data.get(['notes', id], { fields: ['id', 'title'] });
|
|
478
490
|
var mdLink = '[' + linkNote.title + '](:/' + linkNote.id + ')';
|
|
479
|
-
|
|
491
|
+
try {
|
|
492
|
+
await joplin.clipboard.writeText(mdLink);
|
|
493
|
+
} catch(e) {
|
|
494
|
+
// Fallback: send link text to webview for copying
|
|
495
|
+
await joplin.views.panels.postMessage(panel, { name: 'copyText', text: mdLink });
|
|
496
|
+
}
|
|
480
497
|
break;
|
|
481
498
|
case 'duplicateNote':
|
|
482
499
|
var srcNote = await joplin.data.get(['notes', id], { fields: ['title', 'body', 'parent_id', 'is_todo'] });
|
|
483
|
-
await joplin.data.post(['notes'], null, {
|
|
500
|
+
var dupNote = await joplin.data.post(['notes'], null, {
|
|
484
501
|
title: srcNote.title + ' (copy)', body: srcNote.body,
|
|
485
502
|
parent_id: srcNote.parent_id, is_todo: srcNote.is_todo,
|
|
486
503
|
});
|
|
504
|
+
await joplin.commands.execute('openNote', dupNote.id);
|
|
505
|
+
selectedNoteId = dupNote.id;
|
|
487
506
|
break;
|
|
488
507
|
case 'switchNoteType':
|
|
489
508
|
var sn = await joplin.data.get(['notes', id], { fields: ['is_todo'] });
|
package/publish/manifest.json
CHANGED
|
@@ -2,9 +2,17 @@
|
|
|
2
2
|
"manifest_version": 1,
|
|
3
3
|
"id": "com.github.joplin-explorer",
|
|
4
4
|
"app_min_version": "2.6.0",
|
|
5
|
-
"version": "1.1.
|
|
5
|
+
"version": "1.1.2",
|
|
6
6
|
"name": "Joplin Explorer",
|
|
7
7
|
"description": "A unified sidebar that displays notebooks and notes together in a single tree view",
|
|
8
|
-
"author": "
|
|
9
|
-
"homepage_url": "https://github.com"
|
|
8
|
+
"author": "lim0513",
|
|
9
|
+
"homepage_url": "https://github.com/lim0513/joplin-explorer",
|
|
10
|
+
"repository_url": "https://github.com/lim0513/joplin-explorer",
|
|
11
|
+
"keywords": ["sidebar", "explorer", "tree-view", "notebooks", "notes"],
|
|
12
|
+
"categories": ["appearance", "productivity"],
|
|
13
|
+
"screenshots": [
|
|
14
|
+
{"src": "screenshots/tree-view.png", "label": "Unified tree view of notebooks and notes"},
|
|
15
|
+
{"src": "screenshots/search.png", "label": "Full-text search with keyword highlighting"},
|
|
16
|
+
{"src": "screenshots/context-menu.png", "label": "Right-click context menu"}
|
|
17
|
+
]
|
|
10
18
|
}
|
package/publish/plugin.jpl
CHANGED
|
Binary file
|
|
@@ -347,6 +347,76 @@ html, body {
|
|
|
347
347
|
word-break: break-all;
|
|
348
348
|
}
|
|
349
349
|
|
|
350
|
+
/* Inline input dialog */
|
|
351
|
+
.inline-input-overlay {
|
|
352
|
+
position: fixed;
|
|
353
|
+
top: 0; left: 0; right: 0; bottom: 0;
|
|
354
|
+
background: rgba(0, 0, 0, 0.3);
|
|
355
|
+
display: flex;
|
|
356
|
+
align-items: center;
|
|
357
|
+
justify-content: center;
|
|
358
|
+
z-index: 10000;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
.inline-input-dialog {
|
|
362
|
+
background: var(--joplin-background-color, #fff);
|
|
363
|
+
border: 1px solid var(--joplin-divider-color, #ccc);
|
|
364
|
+
border-radius: 8px;
|
|
365
|
+
padding: 16px;
|
|
366
|
+
min-width: 240px;
|
|
367
|
+
max-width: 90%;
|
|
368
|
+
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
.inline-input-label {
|
|
372
|
+
font-size: 12px;
|
|
373
|
+
margin-bottom: 8px;
|
|
374
|
+
color: var(--joplin-color);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
.inline-input-field {
|
|
378
|
+
width: 100%;
|
|
379
|
+
box-sizing: border-box;
|
|
380
|
+
padding: 6px 8px;
|
|
381
|
+
border: 1px solid var(--joplin-divider-color, #ccc);
|
|
382
|
+
border-radius: 4px;
|
|
383
|
+
background: var(--joplin-background-color, #fff);
|
|
384
|
+
color: var(--joplin-color);
|
|
385
|
+
font-size: 13px;
|
|
386
|
+
outline: none;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
.inline-input-field:focus {
|
|
390
|
+
border-color: var(--joplin-color2, #4a9cf5);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
.inline-input-buttons {
|
|
394
|
+
display: flex;
|
|
395
|
+
gap: 8px;
|
|
396
|
+
margin-top: 12px;
|
|
397
|
+
justify-content: flex-end;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.inline-input-buttons button {
|
|
401
|
+
padding: 5px 16px;
|
|
402
|
+
border: 1px solid var(--joplin-divider-color, #ccc);
|
|
403
|
+
border-radius: 4px;
|
|
404
|
+
cursor: pointer;
|
|
405
|
+
font-size: 12px;
|
|
406
|
+
color: var(--joplin-color);
|
|
407
|
+
background: var(--joplin-background-color3, #f0f0f0);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
.inline-input-ok {
|
|
411
|
+
background: var(--joplin-color2, #4a9cf5) !important;
|
|
412
|
+
color: #fff !important;
|
|
413
|
+
border-color: var(--joplin-color2, #4a9cf5) !important;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
.inline-input-buttons button:hover {
|
|
417
|
+
opacity: 0.85;
|
|
418
|
+
}
|
|
419
|
+
|
|
350
420
|
mark.search-highlight {
|
|
351
421
|
background: rgba(255, 213, 0, 0.4);
|
|
352
422
|
color: inherit;
|
package/publish/webview/panel.js
CHANGED
|
@@ -46,6 +46,49 @@ function T(key) {
|
|
|
46
46
|
return (window._i18n && window._i18n[key]) || key;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
// Inline input dialog (replaces prompt() which may not work in webview)
|
|
50
|
+
function showInlineInput(label, defaultValue, callback) {
|
|
51
|
+
var existing = document.getElementById('inline-input-overlay');
|
|
52
|
+
if (existing) existing.remove();
|
|
53
|
+
|
|
54
|
+
var overlay = document.createElement('div');
|
|
55
|
+
overlay.id = 'inline-input-overlay';
|
|
56
|
+
overlay.className = 'inline-input-overlay';
|
|
57
|
+
overlay.innerHTML = '<div class="inline-input-dialog">'
|
|
58
|
+
+ '<div class="inline-input-label">' + label + '</div>'
|
|
59
|
+
+ '<input class="inline-input-field" type="text" value="' + (defaultValue || '').replace(/"/g, '"') + '" />'
|
|
60
|
+
+ '<div class="inline-input-buttons">'
|
|
61
|
+
+ '<button class="inline-input-ok">OK</button>'
|
|
62
|
+
+ '<button class="inline-input-cancel">' + (T('cancel') || 'Cancel') + '</button>'
|
|
63
|
+
+ '</div></div>';
|
|
64
|
+
|
|
65
|
+
document.body.appendChild(overlay);
|
|
66
|
+
|
|
67
|
+
var input = overlay.querySelector('.inline-input-field');
|
|
68
|
+
input.focus();
|
|
69
|
+
input.select();
|
|
70
|
+
|
|
71
|
+
function submit() {
|
|
72
|
+
var val = input.value;
|
|
73
|
+
overlay.remove();
|
|
74
|
+
callback(val);
|
|
75
|
+
}
|
|
76
|
+
function cancel() {
|
|
77
|
+
overlay.remove();
|
|
78
|
+
callback(null);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
overlay.querySelector('.inline-input-ok').addEventListener('click', submit);
|
|
82
|
+
overlay.querySelector('.inline-input-cancel').addEventListener('click', cancel);
|
|
83
|
+
input.addEventListener('keydown', function(e) {
|
|
84
|
+
if (e.key === 'Enter') submit();
|
|
85
|
+
if (e.key === 'Escape') cancel();
|
|
86
|
+
});
|
|
87
|
+
overlay.addEventListener('click', function(e) {
|
|
88
|
+
if (e.target === overlay) cancel();
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
49
92
|
// Left click: open note / toggle folder
|
|
50
93
|
document.addEventListener('click', function(e) {
|
|
51
94
|
var existingMenu = document.getElementById('ctx-menu');
|
|
@@ -133,10 +176,11 @@ document.addEventListener('click', function(e) {
|
|
|
133
176
|
|
|
134
177
|
if (action === 'renameFolder' || action === 'renameNote') {
|
|
135
178
|
var currentTitle = ctxItem.dataset.title || '';
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
179
|
+
showInlineInput(T('promptRename'), currentTitle, function(newTitle) {
|
|
180
|
+
if (newTitle !== null && newTitle.trim() !== '') {
|
|
181
|
+
postMsg({ name: 'contextMenu', action: action, id: id, itemType: itemType, newTitle: newTitle.trim() });
|
|
182
|
+
}
|
|
183
|
+
});
|
|
140
184
|
} else if (action === 'deleteFolder') {
|
|
141
185
|
if (confirm(T('confirmDeleteFolder'))) {
|
|
142
186
|
postMsg({ name: 'contextMenu', action: action, id: id, itemType: itemType });
|
|
@@ -146,10 +190,11 @@ document.addEventListener('click', function(e) {
|
|
|
146
190
|
postMsg({ name: 'contextMenu', action: action, id: id, itemType: itemType });
|
|
147
191
|
}
|
|
148
192
|
} else if (action === 'moveNote') {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
193
|
+
showInlineInput(T('promptMoveNote'), '', function(folderName) {
|
|
194
|
+
if (folderName !== null && folderName.trim() !== '') {
|
|
195
|
+
postMsg({ name: 'contextMenu', action: action, id: id, itemType: itemType, targetFolderName: folderName.trim() });
|
|
196
|
+
}
|
|
197
|
+
});
|
|
153
198
|
} else {
|
|
154
199
|
postMsg({ name: 'contextMenu', action: action, id: id, itemType: itemType });
|
|
155
200
|
}
|
|
@@ -219,6 +264,18 @@ webviewApi.onMessage(function(msg) {
|
|
|
219
264
|
} else {
|
|
220
265
|
renderSearchResults(m.results, m.query);
|
|
221
266
|
}
|
|
267
|
+
} else if (m.name === 'copyText') {
|
|
268
|
+
// Fallback clipboard copy via webview
|
|
269
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
270
|
+
navigator.clipboard.writeText(m.text);
|
|
271
|
+
} else {
|
|
272
|
+
var ta = document.createElement('textarea');
|
|
273
|
+
ta.value = m.text;
|
|
274
|
+
document.body.appendChild(ta);
|
|
275
|
+
ta.select();
|
|
276
|
+
document.execCommand('copy');
|
|
277
|
+
ta.remove();
|
|
278
|
+
}
|
|
222
279
|
} else if (m.name === 'selectNote') {
|
|
223
280
|
// Update selection without full re-render
|
|
224
281
|
document.querySelectorAll('.tree-item.note.selected').forEach(function(el) {
|