joplin-plugin-explorer 1.1.0 → 1.1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "joplin-plugin-explorer",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
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
@@ -328,6 +328,7 @@ joplin.plugins.register({
328
328
  + ' <input id="search-input" type="text" placeholder="\uD83D\uDD0D ' + t.search + '" />'
329
329
  + ' </div>'
330
330
  + ' <div id="tree-container">' + treeHtml + '</div>'
331
+ + ' <div id="search-results" style="display:none;"></div>'
331
332
  + ' <div class="bottom-bar">'
332
333
  + ' <button id="btn-sync" title="' + t.sync + '">\uD83D\uDD04 ' + t.sync + '</button>'
333
334
  + ' </div>'
@@ -370,7 +371,7 @@ joplin.plugins.register({
370
371
  var query = msg.query;
371
372
  if (!query || !query.trim()) {
372
373
  // Empty query: tell webview to clear search results
373
- await joplin.views.panels.postMessage(panel, { name: 'searchResults', results: null, query: '' });
374
+ await joplin.views.panels.postMessage(panel, { name: 'searchResults', results: null, query: '', searchId: msg.searchId });
374
375
  return;
375
376
  }
376
377
  try {
@@ -418,7 +419,7 @@ joplin.plugins.register({
418
419
  folderName: folderNameMap[note.parent_id] || '',
419
420
  });
420
421
  }
421
- await joplin.views.panels.postMessage(panel, { name: 'searchResults', results: items, query: query });
422
+ await joplin.views.panels.postMessage(panel, { name: 'searchResults', results: items, query: query, searchId: msg.searchId });
422
423
  } catch (err) {
423
424
  console.error('Joplin Explorer: search error', err);
424
425
  }
@@ -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.0",
5
+ "version": "1.1.1",
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": "user",
Binary file
@@ -274,6 +274,25 @@ html, body {
274
274
  box-shadow: 0 2px 0 0 var(--joplin-color2, #4a9cf5);
275
275
  }
276
276
 
277
+ #search-results {
278
+ flex: 1;
279
+ overflow-y: scroll;
280
+ padding: 4px 0;
281
+ }
282
+
283
+ #search-results::-webkit-scrollbar {
284
+ width: 8px;
285
+ }
286
+
287
+ #search-results::-webkit-scrollbar-track {
288
+ background: transparent;
289
+ }
290
+
291
+ #search-results::-webkit-scrollbar-thumb {
292
+ background: var(--joplin-color-faded, #888);
293
+ border-radius: 4px;
294
+ }
295
+
277
296
  /* Search results */
278
297
  .search-status {
279
298
  padding: 8px 12px;
@@ -21,7 +21,6 @@ var _observer = new MutationObserver(function() {
21
21
  if (!container) return;
22
22
 
23
23
  if (_isFirstRender) {
24
- // First render: scroll to selected note
25
24
  _isFirstRender = false;
26
25
  var selected = container.querySelector('.tree-item.note.selected');
27
26
  if (selected) {
@@ -30,7 +29,6 @@ var _observer = new MutationObserver(function() {
30
29
  }, 30);
31
30
  }
32
31
  } else {
33
- // Subsequent renders: restore previous scroll position
34
32
  container.scrollTop = _savedScrollTop;
35
33
  }
36
34
  });
@@ -214,8 +212,9 @@ webviewApi.onMessage(function(msg) {
214
212
  }, 2000);
215
213
  }
216
214
  } else if (m.name === 'searchResults') {
215
+ // Ignore stale search results
216
+ if (m.searchId !== undefined && m.searchId !== _searchId) return;
217
217
  if (m.results === null) {
218
- // Cleared search
219
218
  if (_searchMode) exitSearchMode();
220
219
  } else {
221
220
  renderSearchResults(m.results, m.query);
@@ -351,6 +350,7 @@ document.addEventListener('drop', function(e) {
351
350
  // ======================== Content Search ========================
352
351
  var _searchTimer = null;
353
352
  var _searchMode = false;
353
+ var _searchId = 0;
354
354
 
355
355
  function escapeRegex(str) {
356
356
  return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
@@ -364,10 +364,19 @@ function highlightText(text, query) {
364
364
  return escaped.replace(regex, '<mark class="search-highlight">$1</mark>');
365
365
  }
366
366
 
367
+ function showSearchContainer(show) {
368
+ var tree = document.getElementById('tree-container');
369
+ var search = document.getElementById('search-results');
370
+ if (tree) tree.style.display = show ? 'none' : '';
371
+ if (search) search.style.display = show ? '' : 'none';
372
+ }
373
+
367
374
  function renderSearchResults(results, query) {
368
- var container = document.getElementById('tree-container');
375
+ var container = document.getElementById('search-results');
369
376
  if (!container) return;
370
377
 
378
+ showSearchContainer(true);
379
+
371
380
  if (!results || results.length === 0) {
372
381
  container.innerHTML = '<div class="search-status">' + T('searchNoResult') + '</div>';
373
382
  return;
@@ -401,8 +410,7 @@ function renderSearchResults(results, query) {
401
410
 
402
411
  function exitSearchMode() {
403
412
  _searchMode = false;
404
- // Trigger a refresh to restore the tree
405
- postMsg({ name: 'refresh' });
413
+ showSearchContainer(false);
406
414
  }
407
415
 
408
416
  document.addEventListener('input', function(e) {
@@ -410,21 +418,24 @@ document.addEventListener('input', function(e) {
410
418
  var query = e.target.value.trim();
411
419
 
412
420
  if (_searchTimer) clearTimeout(_searchTimer);
421
+ _searchId++;
422
+ var currentSearchId = _searchId;
413
423
 
414
424
  if (!query) {
415
425
  if (_searchMode) exitSearchMode();
416
426
  return;
417
427
  }
418
428
 
419
- // Show "searching..." immediately
420
- var container = document.getElementById('tree-container');
421
- if (container) {
422
- container.innerHTML = '<div class="search-status">' + T('searching') + '</div>';
429
+ _searchMode = true;
430
+ var searchContainer = document.getElementById('search-results');
431
+ if (searchContainer) {
432
+ searchContainer.innerHTML = '<div class="search-status">' + T('searching') + '</div>';
423
433
  }
434
+ showSearchContainer(true);
424
435
 
425
436
  // Debounce: wait 400ms after typing stops
426
437
  _searchTimer = setTimeout(function() {
427
- postMsg({ name: 'search', query: query });
438
+ postMsg({ name: 'search', query: query, searchId: currentSearchId });
428
439
  }, 400);
429
440
  });
430
441