joplin-plugin-explorer 1.1.2 → 1.1.4

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-explorer",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "A unified sidebar that displays notebooks and notes together in a single tree view",
5
5
  "author": "lim0513",
6
6
  "homepage": "https://github.com/lim0513/joplin-explorer",
package/publish/index.js CHANGED
@@ -254,12 +254,77 @@ joplin.plugins.register({
254
254
  await joplin.views.panels.setHtml(panel, '<div id="notes-in-list-root"><p style="padding:12px;">' + t.loading + '</p></div>');
255
255
  await joplin.views.panels.show(panel, true);
256
256
 
257
+ // Native dialogs
258
+ var inputDialog = await joplin.views.dialogs.create('explorerInputDialog');
259
+ var confirmDialog = await joplin.views.dialogs.create('explorerConfirmDialog');
260
+ var infoDialog = await joplin.views.dialogs.create('explorerInfoDialog');
261
+
262
+ async function showNativeConfirm(message) {
263
+ await joplin.views.dialogs.setHtml(confirmDialog,
264
+ '<div style="padding:10px;min-width:280px;">'
265
+ + '<div style="font-size:13px;">' + escapeHtml(message) + '</div>'
266
+ + '</div>'
267
+ );
268
+ await joplin.views.dialogs.setButtons(confirmDialog, [
269
+ { id: 'ok', title: 'OK' },
270
+ { id: 'cancel', title: t.cancel || 'Cancel' },
271
+ ]);
272
+ var result = await joplin.views.dialogs.open(confirmDialog);
273
+ return result.id === 'ok';
274
+ }
275
+
276
+ async function showNativeInfo(title, body) {
277
+ await joplin.views.dialogs.setHtml(infoDialog,
278
+ '<div style="padding:10px;min-width:320px;">'
279
+ + '<div style="font-size:14px;font-weight:bold;margin-bottom:10px;">' + escapeHtml(title) + '</div>'
280
+ + '<div style="font-size:12px;line-height:1.8;white-space:pre-wrap;">' + escapeHtml(body) + '</div>'
281
+ + '</div>'
282
+ );
283
+ await joplin.views.dialogs.setButtons(infoDialog, [
284
+ { id: 'ok', title: 'OK' },
285
+ ]);
286
+ await joplin.views.dialogs.open(infoDialog);
287
+ }
288
+
289
+ async function showNativeInput(label, defaultValue) {
290
+ await joplin.views.dialogs.setHtml(inputDialog,
291
+ '<div style="padding:10px;min-width:300px;">'
292
+ + '<div style="margin-bottom:8px;font-size:13px;">' + escapeHtml(label) + '</div>'
293
+ + '<form name="inputForm">'
294
+ + '<input name="value" type="text" value="' + escapeHtml(defaultValue || '') + '" '
295
+ + 'style="width:100%;box-sizing:border-box;padding:6px 8px;font-size:13px;" />'
296
+ + '</form>'
297
+ + '</div>'
298
+ );
299
+ await joplin.views.dialogs.setButtons(inputDialog, [
300
+ { id: 'ok', title: 'OK' },
301
+ { id: 'cancel', title: t.cancel || 'Cancel' },
302
+ ]);
303
+ var result = await joplin.views.dialogs.open(inputDialog);
304
+ if (result.id === 'ok' && result.formData && result.formData.inputForm) {
305
+ return result.formData.inputForm.value || null;
306
+ }
307
+ return null;
308
+ }
309
+
257
310
  var selectedNoteId = '';
258
311
  var collapsedFolders = {};
259
312
  var currentSort = 'updated_desc';
260
313
  var allFoldersCache = [];
261
314
  var isFirstLoad = true;
262
315
 
316
+ function expandToFolder(folderId) {
317
+ var parentId = folderId;
318
+ while (parentId) {
319
+ delete collapsedFolders[parentId];
320
+ var found = null;
321
+ for (var i = 0; i < allFoldersCache.length; i++) {
322
+ if (allFoldersCache[i].id === parentId) { found = allFoldersCache[i]; break; }
323
+ }
324
+ parentId = found ? found.parent_id : null;
325
+ }
326
+ }
327
+
263
328
  function sortNotes(notes, sortMode) {
264
329
  var sorted = notes.slice();
265
330
  switch (sortMode) {
@@ -367,9 +432,13 @@ joplin.plugins.register({
367
432
  await refreshPanel();
368
433
  } else if (msg.name === 'newNote') {
369
434
  await joplin.commands.execute('newNote');
435
+ var nn = await joplin.workspace.selectedNote();
436
+ if (nn) { selectedNoteId = nn.id; expandToFolder(nn.parent_id); }
370
437
  await refreshPanel();
371
438
  } else if (msg.name === 'newTodo') {
372
439
  await joplin.commands.execute('newTodo');
440
+ var nt = await joplin.workspace.selectedNote();
441
+ if (nt) { selectedNoteId = nt.id; expandToFolder(nt.parent_id); }
373
442
  await refreshPanel();
374
443
  } else if (msg.name === 'search') {
375
444
  var query = msg.query;
@@ -454,20 +523,32 @@ joplin.plugins.register({
454
523
  var newNote = await joplin.data.post(['notes'], null, { title: t.newNote, parent_id: id });
455
524
  await joplin.commands.execute('openNote', newNote.id);
456
525
  selectedNoteId = newNote.id;
526
+ expandToFolder(id);
457
527
  break;
458
528
  case 'newTodo':
459
529
  var newTodo = await joplin.data.post(['notes'], null, { title: t.newTodo, parent_id: id, is_todo: 1 });
460
530
  await joplin.commands.execute('openNote', newTodo.id);
461
531
  selectedNoteId = newTodo.id;
532
+ expandToFolder(id);
462
533
  break;
463
534
  case 'newSubNotebook':
464
- await joplin.data.post(['folders'], null, { title: t.newNotebook, parent_id: id });
535
+ var subName = await showNativeInput(t.newNotebook, '');
536
+ if (subName && subName.trim()) {
537
+ await joplin.data.post(['folders'], null, { title: subName.trim(), parent_id: id });
538
+ }
465
539
  break;
466
540
  case 'deleteFolder':
467
- await joplin.data.delete(['folders', id]);
541
+ var folderInfo = await joplin.data.get(['folders', id], { fields: ['title'] });
542
+ if (await showNativeConfirm(t.confirmDeleteFolder + '\n\n' + folderInfo.title)) {
543
+ await joplin.data.delete(['folders', id]);
544
+ }
468
545
  break;
469
546
  case 'renameFolder':
470
- if (msg.newTitle) await joplin.data.put(['folders', id], null, { title: msg.newTitle });
547
+ var folderData = await joplin.data.get(['folders', id], { fields: ['title'] });
548
+ var newFolderName = await showNativeInput(t.promptRename, folderData.title);
549
+ if (newFolderName && newFolderName.trim()) {
550
+ await joplin.data.put(['folders', id], null, { title: newFolderName.trim() });
551
+ }
471
552
  break;
472
553
  case 'exportFolder':
473
554
  try { await joplin.commands.execute('exportFolders', [id]); } catch(e) {}
@@ -515,7 +596,11 @@ joplin.plugins.register({
515
596
  }
516
597
  break;
517
598
  case 'renameNote':
518
- if (msg.newTitle) await joplin.data.put(['notes', id], null, { title: msg.newTitle });
599
+ var noteData = await joplin.data.get(['notes', id], { fields: ['title'] });
600
+ var newNoteName = await showNativeInput(t.promptRename, noteData.title);
601
+ if (newNoteName && newNoteName.trim()) {
602
+ await joplin.data.put(['notes', id], null, { title: newNoteName.trim() });
603
+ }
519
604
  break;
520
605
  case 'moveNote':
521
606
  if (msg.targetFolderName) {
@@ -531,21 +616,26 @@ joplin.plugins.register({
531
616
  }
532
617
  break;
533
618
  case 'noteInfo':
534
- var info = await joplin.data.get(['notes', id], { fields: ['id', 'title', 'created_time', 'updated_time', 'is_todo', 'parent_id'] });
535
- var parentTitle = '';
619
+ var info = await joplin.data.get(['notes', id], { fields: ['id', 'title', 'created_time', 'updated_time', 'is_todo', 'parent_id', 'body'] });
620
+ var pTitle = '';
536
621
  for (var i = 0; i < allFoldersCache.length; i++) {
537
- if (allFoldersCache[i].id === info.parent_id) { parentTitle = allFoldersCache[i].title; break; }
622
+ if (allFoldersCache[i].id === info.parent_id) { pTitle = allFoldersCache[i].title; break; }
538
623
  }
539
- var infoText = 'ID: ' + info.id
540
- + '\nTitle: ' + info.title
541
- + '\nNotebook: ' + parentTitle
542
- + '\nCreated: ' + new Date(info.created_time).toLocaleString()
543
- + '\nUpdated: ' + new Date(info.updated_time).toLocaleString()
544
- + '\nType: ' + (info.is_todo ? 'To-do' : 'Note');
545
- // Send info back to webview to display
546
- return { name: 'showInfo', text: infoText };
624
+ var bodyLen = (info.body || '').length;
625
+ var infoBody = 'ID: ' + info.id
626
+ + '\n' + (t.ctxRenameNote || 'Title') + ': ' + info.title
627
+ + '\n' + (t.newNotebook || 'Notebook') + ': ' + pTitle
628
+ + '\n' + (t.sortUpdatedDesc ? t.sortUpdatedDesc.replace(/[↓↑\u2193\u2191]\s*/, '') : 'Created') + ': ' + new Date(info.created_time).toLocaleString()
629
+ + '\n' + (t.sortUpdatedAsc ? t.sortUpdatedAsc.replace(/[↓↑\u2193\u2191]\s*/, '') : 'Updated') + ': ' + new Date(info.updated_time).toLocaleString()
630
+ + '\nType: ' + (info.is_todo ? 'To-do' : 'Note')
631
+ + '\n' + 'Size: ' + bodyLen + ' chars';
632
+ await showNativeInfo(info.title, infoBody);
633
+ break;
547
634
  case 'deleteNote':
548
- await joplin.data.delete(['notes', id]);
635
+ var noteForDel = await joplin.data.get(['notes', id], { fields: ['title'] });
636
+ if (await showNativeConfirm(t.confirmDeleteNote + '\n\n' + noteForDel.title)) {
637
+ await joplin.data.delete(['notes', id]);
638
+ }
549
639
  break;
550
640
  }
551
641
  }
@@ -2,7 +2,7 @@
2
2
  "manifest_version": 1,
3
3
  "id": "com.github.joplin-explorer",
4
4
  "app_min_version": "2.6.0",
5
- "version": "1.1.2",
5
+ "version": "1.1.4",
6
6
  "name": "Joplin Explorer",
7
7
  "description": "A unified sidebar that displays notebooks and notes together in a single tree view",
8
8
  "author": "lim0513",
Binary file
@@ -408,9 +408,9 @@ html, body {
408
408
  }
409
409
 
410
410
  .inline-input-ok {
411
- background: var(--joplin-color2, #4a9cf5) !important;
411
+ background: #1a73e8 !important;
412
412
  color: #fff !important;
413
- border-color: var(--joplin-color2, #4a9cf5) !important;
413
+ border-color: #1a73e8 !important;
414
414
  }
415
415
 
416
416
  .inline-input-buttons button:hover {
@@ -150,7 +150,6 @@ document.addEventListener('contextmenu', function(e) {
150
150
  menuHtml += '<div class="ctx-item" data-action="toggleTodo" data-id="' + id + '" data-type="note">' + T('ctxToggleTodo') + '</div>';
151
151
  menuHtml += '<div class="ctx-sep"></div>';
152
152
  menuHtml += '<div class="ctx-item" data-action="renameNote" data-id="' + id + '" data-type="note" data-title="' + title.replace(/"/g, '&quot;') + '">' + T('ctxRenameNote') + '</div>';
153
- menuHtml += '<div class="ctx-item" data-action="moveNote" data-id="' + id + '" data-type="note">' + T('ctxMoveNote') + '</div>';
154
153
  menuHtml += '<div class="ctx-item" data-action="noteInfo" data-id="' + id + '" data-type="note">' + T('ctxNoteInfo') + '</div>';
155
154
  menuHtml += '<div class="ctx-sep"></div>';
156
155
  menuHtml += '<div class="ctx-item ctx-danger" data-action="deleteNote" data-id="' + id + '" data-type="note">' + T('ctxDeleteNote') + '</div>';
@@ -174,30 +173,8 @@ document.addEventListener('click', function(e) {
174
173
  var id = ctxItem.dataset.id;
175
174
  var itemType = ctxItem.dataset.type;
176
175
 
177
- if (action === 'renameFolder' || action === 'renameNote') {
178
- var currentTitle = ctxItem.dataset.title || '';
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
- });
184
- } else if (action === 'deleteFolder') {
185
- if (confirm(T('confirmDeleteFolder'))) {
186
- postMsg({ name: 'contextMenu', action: action, id: id, itemType: itemType });
187
- }
188
- } else if (action === 'deleteNote') {
189
- if (confirm(T('confirmDeleteNote'))) {
190
- postMsg({ name: 'contextMenu', action: action, id: id, itemType: itemType });
191
- }
192
- } else if (action === 'moveNote') {
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
- });
198
- } else {
199
- postMsg({ name: 'contextMenu', action: action, id: id, itemType: itemType });
200
- }
176
+ // All dialogs handled by backend using native Joplin dialogs
177
+ postMsg({ name: 'contextMenu', action: action, id: id, itemType: itemType });
201
178
 
202
179
  var menu = document.getElementById('ctx-menu');
203
180
  if (menu) menu.remove();