fix: preserve custom OCR engine cards

This commit is contained in:
mARTin-B78 2026-09-05 08:11:57 +02:00
parent 60ef436ce1
commit 6553d5003d
4 changed files with 33 additions and 14 deletions

View File

@ -8,6 +8,9 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · versioned wi
## [Unreleased]
### Changed
- **OCR custom-card persistence.** Browser-local and server-backed custom OCR profiles are now merged during reloads,
so a newly added Baidu or other OCR engine remains visible while settings synchronize; the OCR page also shows an
actionable empty state when no profiles exist.
- **OCR engine category.** Engines now includes local OCR/document-parser profiles for KDL Frontier Parser nano and
Baidu Unlimited-OCR, with custom OCR profiles, connection probes, page-image API guidance, and future Magazine
workflow integration documented in the TODO roadmap.

View File

@ -1 +1 @@
1.25.2
1.25.3

View File

@ -10,7 +10,7 @@
<meta name="format-detection" content="telephone=no">
<meta name="color-scheme" content="light dark">
<meta name="theme-color" content="#2563EB">
<meta name="app-version" content="1.25.2">
<meta name="app-version" content="1.25.3">
<link rel="manifest" href="/manifest.webmanifest">
<link rel="icon" href="/static/icon.svg" type="image/svg+xml">
<link rel="apple-touch-icon" href="/static/icon.svg">
@ -27,12 +27,12 @@
<!-- ── Core styles (local — no CDN dependency for first paint) ────────── -->
<link rel="stylesheet" href="/static/vendor/mdi/materialdesignicons.min.css">
<link rel="stylesheet" href="/static/style.css?v=1.25.2">
<link rel="stylesheet" href="/static/css/resource-diagnostics.css?v=1.25.2">
<link rel="stylesheet" href="/static/css/background-jobs.css?v=1.25.2">
<link rel="stylesheet" href="/static/css/voice-design.css?v=1.25.2">
<link rel="stylesheet" href="/static/css/rehearser.css?v=1.25.2">
<link rel="stylesheet" href="/static/css/audiobook.css?v=1.25.2">
<link rel="stylesheet" href="/static/style.css?v=1.25.3">
<link rel="stylesheet" href="/static/css/resource-diagnostics.css?v=1.25.3">
<link rel="stylesheet" href="/static/css/background-jobs.css?v=1.25.3">
<link rel="stylesheet" href="/static/css/voice-design.css?v=1.25.3">
<link rel="stylesheet" href="/static/css/rehearser.css?v=1.25.3">
<link rel="stylesheet" href="/static/css/audiobook.css?v=1.25.3">
<!-- ── Flag icons — non-blocking (loaded async, icons appear after JS) ── -->
@ -398,7 +398,7 @@ window.toggleNavTree = function(treeId, chevronId) {
</script>
<!-- loader.js: fetches sections → loads JS modules → removes skeleton -->
<script src="/static/loader.js?v=1.25.2"></script>
<script src="/static/loader.js?v=1.25.3"></script>
</body>
</html>

View File

@ -236,10 +236,21 @@ if (document.getElementById('s-llms')?.classList.contains('is-active')) {
// ── Custom engine card storage helpers ────────────────────────────────────
function loadCustomEngineCards() {
if (_appSettings && Array.isArray(_appSettings.custom_engine_cards) && _appSettings.custom_engine_cards.length)
return _appSettings.custom_engine_cards;
try { return JSON.parse(localStorage.getItem('engines-custom-cards') || '[]'); }
catch (e) { return []; }
let local = [];
try { local = JSON.parse(localStorage.getItem('engines-custom-cards') || '[]'); }
catch (e) { local = []; }
const server = _appSettings && Array.isArray(_appSettings.custom_engine_cards)
? _appSettings.custom_engine_cards : [];
// Keep older browser-local cards visible while the server settings migrate
// or while a just-added card is still being persisted. Server entries win
// on the same id/name so edits do not produce duplicate cards.
const merged = [...server];
const seen = new Set(merged.map(c => String(c?.id || c?.name || '').toLowerCase()).filter(Boolean));
for (const card of local) {
const key = String(card?.id || card?.name || '').toLowerCase();
if (key && !seen.has(key)) { merged.push(card); seen.add(key); }
}
return merged;
}
function saveCustomEngineCards(cards) {
localStorage.setItem('engines-custom-cards', JSON.stringify(cards));
@ -622,7 +633,12 @@ function renderLocalContainers(containers) {
if (gridTts) gridTts.innerHTML = containers.filter(isTts).map(buildCardHtml).join('');
if (gridStt) gridStt.innerHTML = containers.filter(isStt).map(buildCardHtml).join('');
if (gridLlm) gridLlm.innerHTML = containers.filter(isLlm).map(buildCardHtml).join('');
if (gridOcr) gridOcr.innerHTML = containers.filter(isOcr).map(buildCardHtml).join('');
if (gridOcr) {
const ocrCards = containers.filter(isOcr);
gridOcr.innerHTML = ocrCards.length
? ocrCards.map(buildCardHtml).join('')
: '<div class="dc-loading">No OCR profiles configured yet. Use <strong>Add Custom</strong> to add one.</div>';
}
[gridTts, gridStt, gridLlm, gridOcr].forEach(grid => {
if (!grid) return;