// ── 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); }); 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') libraryRenderCharacters(); }; // ── Books (server-side reader library) ──────────────────────────────────────── function _libSkeleton(n) { return Array.from({ length: n }, () => '
No books yet.
' + 'Open Read Aloud, import a PDF or text, and save it to your library.
No theater plays yet.
' + 'Open Script Rehearsal → Import / Export to add a script, or cast a book as an audiobook.
No characters yet.
' + 'Open a book in Read Aloud, cast it as an audiobook, then click Cast Characters to generate character sheets — or import an existing cast from SillyTavern.
' + '