|
7 | 7 | const ASYNC_PIPELINE_THRESHOLD = 500; |
8 | 8 |
|
9 | 9 | // RESTORED: Helper to update index map |
10 | | -// OPTIMIZED: Avoid creating a full sorted copy for large datasets |
| 10 | +// Maps each item's ID to its sequential position (1-based) when sorted by ID. |
| 11 | +// This provides a stable "original order" number for each card regardless of |
| 12 | +// current sort/filter, used for "Go To Card #" and the #N tag on cards. |
11 | 13 | function refreshIndices() { |
12 | 14 | if (!activeData) return; |
13 | | - |
14 | | - if (activeData.length > ASYNC_PIPELINE_THRESHOLD) { |
15 | | - // For large datasets, build the map by finding min ID and computing offsets |
16 | | - // Since IDs are sequential integers, we can avoid sorting entirely |
17 | | - let minId = Infinity; |
18 | | - for (let i = 0; i < activeData.length; i++) { |
19 | | - if (activeData[i].id < minId) minId = activeData[i].id; |
20 | | - } |
21 | | - idToIndexMap = new Map(); |
22 | | - for (let i = 0; i < activeData.length; i++) { |
23 | | - idToIndexMap.set(activeData[i].id, activeData[i].id - minId + 1); |
24 | | - } |
25 | | - } else { |
26 | | - const sorted = activeData.slice().sort((a, b) => a.id - b.id); |
27 | | - idToIndexMap = new Map(sorted.map((item, index) => [item.id, index + 1])); |
28 | | - } |
| 15 | + const sorted = activeData.slice().sort((a, b) => a.id - b.id); |
| 16 | + idToIndexMap = new Map(sorted.map((item, index) => [item.id, index + 1])); |
29 | 17 | } |
30 | 18 |
|
31 | 19 | // Generate cache key from current filters |
32 | 20 | function getFilterKey() { |
33 | 21 | const parts = [ |
34 | 22 | currentSort, |
| 23 | + currentSecondarySort, |
35 | 24 | topFilters.showFavorites ? '1' : '0', |
36 | 25 | topFilters.showNonFavorited ? '1' : '0', |
37 | 26 | topFilters.showRejected ? '1' : '0', |
@@ -323,48 +312,25 @@ function executePipeline() { |
323 | 312 | return true; |
324 | 313 | }); |
325 | 314 |
|
326 | | - // For large datasets, use async sorting to avoid blocking the UI |
327 | | - if (processedData.length > ASYNC_PIPELINE_THRESHOLD) { |
328 | | - // Cancel any previous async sort |
329 | | - const thisRunId = ++pipelineAbortId; |
330 | | - |
331 | | - // For "oldest" and "newest" sorts, we can use a fast path since items from |
332 | | - // activeData are already ordered by insertion (id order) |
333 | | - if ((currentSort === 'oldest' || currentSort === 'newest') && currentSecondarySort === 'none') { |
334 | | - // Fast path: items from activeData are already in insertion order |
335 | | - // For oldest: a.id - b.id (natural order from activeData) |
336 | | - // For newest: reverse |
337 | | - if (currentSort === 'newest') { |
338 | | - processedData.reverse(); |
339 | | - } |
340 | | - // No expensive sort needed! |
341 | | - } else { |
342 | | - // Async sort: sort in a yielding fashion to keep UI responsive |
343 | | - runMultiSort(processedData); |
344 | | - } |
345 | | - |
346 | | - const elapsed = (performance.now() - startTime).toFixed(1); |
347 | | - console.log(`[Pipeline] Processed ${processedData.length} items in ${elapsed}ms (large dataset mode)`); |
348 | | - updateJSONs(processedData); |
349 | | - |
350 | | - if (filtersChanged) { |
351 | | - if(typeof renderDOM === 'function') renderDOM(); |
352 | | - } else { |
353 | | - if (typeof updateVisibleItems === 'function') updateVisibleItems(); |
| 315 | + // Apply sorting |
| 316 | + // Fast path: for oldest/newest with no secondary sort, items from activeData.filter() |
| 317 | + // are already in ID order — just reverse for newest |
| 318 | + if ((currentSort === 'oldest' || currentSort === 'newest') && currentSecondarySort === 'none') { |
| 319 | + if (currentSort === 'newest') { |
| 320 | + processedData.reverse(); |
354 | 321 | } |
355 | 322 | } else { |
356 | | - // Small dataset: synchronous sort (fast enough) |
357 | 323 | runMultiSort(processedData); |
| 324 | + } |
358 | 325 |
|
359 | | - const elapsed = (performance.now() - startTime).toFixed(1); |
360 | | - console.log(`[Pipeline] Processed ${processedData.length} items in ${elapsed}ms`); |
361 | | - updateJSONs(processedData); |
| 326 | + const elapsed = (performance.now() - startTime).toFixed(1); |
| 327 | + console.log(`[Pipeline] Processed ${processedData.length} items in ${elapsed}ms`); |
| 328 | + updateJSONs(processedData); |
362 | 329 |
|
363 | | - if (filtersChanged) { |
364 | | - if(typeof renderDOM === 'function') renderDOM(); |
365 | | - } else { |
366 | | - if (typeof updateVisibleItems === 'function') updateVisibleItems(); |
367 | | - } |
| 330 | + if (filtersChanged) { |
| 331 | + if(typeof renderDOM === 'function') renderDOM(); |
| 332 | + } else { |
| 333 | + if (typeof updateVisibleItems === 'function') updateVisibleItems(); |
368 | 334 | } |
369 | 335 | } |
370 | 336 |
|
|
0 commit comments