(filePath)
| 1441 | function pruneZipArchiveExpandedGroups(models) { |
| 1442 | if (!zipArchiveExpandedGroups.size) return; |
| 1443 | const pathSets = new Map(); |
| 1444 | for (const model of models || []) { |
| 1445 | const parsed = parseZipPath(model?.filePath || ''); |
| 1446 | if (!parsed.isZipEntry || !parsed.zipPath) continue; |
| 1447 | const gk = `zip:${normalizePathForComparison(parsed.zipPath)}`; |
| 1448 | if (!gk || gk === 'zip:') continue; |
| 1449 | const pathKey = normalizePathForComparison(model.filePath || ''); |
| 1450 | if (!pathKey) continue; |
| 1451 | if (!pathSets.has(gk)) pathSets.set(gk, new Set()); |
| 1452 | pathSets.get(gk).add(pathKey); |
| 1453 | } |
| 1454 | for (const key of [...zipArchiveExpandedGroups]) { |
| 1455 | const set = pathSets.get(key); |
| 1456 | if (!set || set.size < 2) { |
| 1457 | zipArchiveExpandedGroups.delete(key); |
| 1458 | } |
| 1459 | } |
| 1460 | } |
| 1461 | |
| 1462 | /** True when the model should show the "New" badge (SQLite 1, boolean, or string "1"). */ |
| 1463 | function isModelNew(model) { |
| 1464 | if (!model) return false; |
| 1465 | const v = model.isNew; |
| 1466 | return v === 1 || v === true || v === '1'; |
| 1467 | } |
| 1468 | |
| 1469 | /** Keep the thumbnail "New" pill in sync with model.isNew (create, update, virtual-grid reuse). */ |
| 1470 | function syncModelNewBadge(fileItem, model) { |
| 1471 | if (!fileItem) return; |
| 1472 | fileItem.querySelector(':scope > .new-status')?.remove(); |
| 1473 | const thumbHost = |
| 1474 | fileItem.querySelector('.thumbnail-wrapper .thumbnail-container') || |
| 1475 | fileItem.querySelector('.thumbnail-container'); |
| 1476 | if (!thumbHost) return; |
| 1477 | const existingBadge = thumbHost.querySelector(':scope > .new-status'); |
| 1478 | if (isModelNew(model)) { |
| 1479 | if (!existingBadge) { |
| 1480 | const newStatusEl = document.createElement('div'); |
| 1481 | newStatusEl.className = 'new-status'; |
| 1482 | newStatusEl.textContent = 'New'; |
| 1483 | newStatusEl.title = 'New model — clears once you edit it'; |
| 1484 | thumbHost.appendChild(newStatusEl); |
| 1485 | } |
| 1486 | } else if (existingBadge) { |
| 1487 | existingBadge.remove(); |
| 1488 | } |
| 1489 | } |
| 1490 | |
| 1491 | /** Merge fresh model into virtual grid's currentModels when paths match (normalized). */ |
| 1492 | function mergeModelIntoGridCurrentModels(model) { |
| 1493 | const container = document.querySelector('.file-grid'); |
| 1494 | if (!container || !container.currentModels || !model) return false; |
| 1495 | const target = normalizePathForComparison(model.filePath || ''); |
| 1496 | if (!target) return false; |
| 1497 | const idx = container.currentModels.findIndex(m => |
| 1498 | normalizePathForComparison(m.filePath || m.id || '') === target |
| 1499 | ); |
| 1500 | if (idx === -1) return false; |
no test coverage detected