// ── Combined Library — Books · Theater Plays · Characters/Cast ────────────────
//
// One home for every production. Books live server-side (reader_library),
// plays live client-side (IndexedDB reh-library), characters live client-side
// (IndexedDB character-library). They are joined by their normalized title —
// see prodKey() — so a book and a play with the same title are one production
// and share the character cast. This module owns the s-library section and
// provides the cross-links that open a production in Read Aloud or the Rehearser.
const LIB_READER_API = '/api/reader/docs';
// The universal join key across books, plays and characters (mirrors clKey).
function prodKey(title) { return String(title || '').trim().toLowerCase(); }
window._libraryView = (function () {
try { return localStorage.getItem('ttsvc_library_view') || 'books'; } catch (_) { return 'books'; }
})();
window.navLibraryView = function (view) {
if (typeof navTo === 'function') navTo('s-library');
window._libraryView = view;
try { localStorage.setItem('ttsvc_library_view', view); } catch (_) {}
document.querySelectorAll('[data-library-view]').forEach(function (el) {
el.classList.toggle('is-active', el.dataset.libraryView === view);
});
document.querySelectorAll('[data-library-panel]').forEach(function (el) {
el.classList.toggle('is-active', el.dataset.libraryPanel === view);
});
// Characters/Cast is the pipeline's "Cast Characters" stop — keep the
// stepper's current-step highlight in sync when the tab is opened directly.
if (view === 'characters' && typeof refreshWorkflowCrumbs === 'function') refreshWorkflowCrumbs('chars');
libraryRender(view);
};
window.libraryRender = function (view) {
view = view || window._libraryView || 'books';
// Reflect the active tab/panel even when entered via the section hook
document.querySelectorAll('[data-library-view]').forEach(function (el) {
el.classList.toggle('is-active', el.dataset.libraryView === view);
});
document.querySelectorAll('[data-library-panel]').forEach(function (el) {
el.classList.toggle('is-active', el.dataset.libraryPanel === view);
});
if (view === 'books') libraryRenderBooks();
else if (view === 'plays') libraryRenderPlays();
else if (view === 'characters' && typeof window.libraryRenderCharacters === 'function') window.libraryRenderCharacters();
};
// ── Books (server-side reader library) ────────────────────────────────────────
function _libSkeleton(n) {
return Array.from({ length: n }, () =>
'
'
+ ''
+ ''
+ ''
+ '
'
).join('');
}
async function libraryRenderBooks() {
const list = document.getElementById('lib-books-list');
if (!list) return;
list.innerHTML = _libSkeleton(4);
let all = [];
try { const r = await fetch(LIB_READER_API); if (r.ok) all = (await r.json()).docs || []; } catch (_) { all = []; }
if (!all.length) {
list.innerHTML = '
No books yet.
'
+ '
Open Read Aloud, import a PDF or text, and save it to your library.
';
}).join('');
list.querySelectorAll('.lib-book').forEach(function (el) {
const id = el.dataset.id, title = el.dataset.title;
el.addEventListener('click', function (e) {
if (e.target.closest('.reh-book-act')) return;
// Show the reader on the PDF view immediately — an explicit start view
// also skips nav.js's cast-session restore, so a freshly-opened book
// never lands in a stale casting panel.
window._readerStartView = 'main';
if (typeof navTo === 'function') navTo('s-reader');
if (typeof readerOpenLibraryDoc === 'function') readerOpenLibraryDoc(id);
});
const reh = el.querySelector('.lib-act-rehearse');
if (reh) reh.addEventListener('click', function (e) { e.stopPropagation(); productionOpenInRehearser(title); });
const del = el.querySelector('.lib-act-del-book');
if (del) del.addEventListener('click', function (e) {
e.stopPropagation();
libConfirmDelete(el, 'Delete book?', 'Audio files will be removed.', async function () {
try { const r = await fetch(LIB_READER_API + '/' + id, { method: 'DELETE' }); if (!r.ok) throw new Error('delete failed'); toast('Book deleted', 'success'); libraryRenderBooks(); }
catch (err) { toast(err.message || 'Delete failed', 'error'); }
});
});
});
}
// ── Theater Plays (client-side rehearser library) ─────────────────────────────
async function libraryRenderPlays() {
const list = document.getElementById('lib-plays-list');
if (!list) return;
list.innerHTML = _libSkeleton(3);
let all = [];
try { all = (typeof rehDbGetAll === 'function') ? await rehDbGetAll() : []; } catch (_) { all = []; }
if (!all.length) {
list.innerHTML = '
No theater plays yet.
'
+ '
Open Script Rehearsal → Import / Export to add a script, or cast a book as an audiobook.