Skip to content

Mundus v0.8.3

Choose a tag to compare

@NotBedd NotBedd released this 22 Oct 20:08
· 29 commits to main since this release

v0.8.3 Finally brings full book export functionality, though not deeply tested yet, the core is here!

Mundus v0.8.3 — Worklog

NEW FEATURES — Export Books

  1. Reordering mode in Mundus View (all platforms)

    Data model (backwards-compatible, no migration):
    NovelDocument.bookOrderIndex?: number — order of docs WITHIN their scope (their folder, or the loose block).
    Folder.orderIndex (pre-existing) — folder order.
    MundusProject.looseDocsPosition?: 'top' | 'bottom' — where the loose files block sits. Default bottom = the historical layout (the unfiled section always rendered after folders).
    New lib src/lib/bookOrder.ts: resolveBookLayout / sortByBookOrder / sortFoldersForBook / bookDocumentSequence / adjacentSwapReindex.
    Resolution rule: unindexed items sort AFTER indexed ones with stable array order. Consequence: an untouched profile displays byte-identically to v0.8.2; newly created documents land at the END of their scope until the author places them.
    UI: Reorder toggle button in the Mundus View header (id btn-start-reorder). In reorder mode: a banner (id reorder-banner) with instructions + loose-files Top/Bottom toggle living right there (btn-loose-position-top/bottom, persisted via onUpdateProject → handleUpdateMundusProject → cloud) + Done button. Search/filter bar hides during reorder.
    Folder headers get ▲▼ (adjacent swap, orderIndex reindexed 0..n-1 per move, saved whole-object via new handleUpdateFolder in App.tsx → saveUserFolder).
    Document rows get ▲▼ (reorderControls prop on DocumentRow; bookOrderIndex reindexed per move via existing onUpdateDocument → handleUpdateDocument → cloud).
    The resolved order is the Mundus View display order too (normal mode applies search/status filters on top of it; flat "All Files" view keeps its historical array order).
    Order persists across reload (localStorage + Firebase whole-object writes carry the new fields automatically; no field whitelists exist for docs/folders/projects — verified).

  2. Export Book (dialog src/components/BookExportDialog.tsx, engine src/lib/bookExport.ts)

    "Export Book" button in the Mundus View header (id btn-start-export-book) → dialog with:
    Format picker: PDF / DOCX / EPUB (separate options, as requested).
    Book title (defaults to project name) + Author name (remembered in mundus_book_export_prefs).
    Toggles: title page (default on), chapter list page/TOC (default on), document titles as chapter headings (default on).
    Toggle list in book order with folder groups: folders appear as tri-state checkbox headers (toggle whole folder), documents individually toggleable, loose files as their own group positioned top/bottom per project setting. Word counts per row.
    Export order = the reorder mode order (bookDocumentSequence: loose top/bottom → folder groups → loose).
    PDF: Electron path = one combined printable HTML (buildBookPrintableHtml → buildPrintableHtmlShell, extracted verbatim from buildFullPrintableHtml) → existing native electronAPI.exportPdf (native save dialog). Web/Android path = per-chapter buildPaperClone (same pagination engine as Writing Mode) appended into ONE jsPDF via new appendPaperCloneToPdf (extracted from exportToPdf, behavior identical). Title page + TOC rendered as paper pages in the same visual language. PDF metadata: title + author. Book PDFs intentionally do NOT embed the single-doc MUNDUS1 re-import payload.
    DOCX: per-chapter htmlToDocxParagraphs (the v0.8.1 HTML→Word converter, extracted verbatim from exportToDocx — single-doc export behavior unchanged) + page break between chapters + title page + Contents page. Word core properties: title + creator.
    EPUB 3 (jszip — new dependency, installed via npm): mimetype STORED first, META-INF/container.xml, OEBPS/ with content.opf (dc:title/dc:creator/dcterms:modified), nav.xhtml (EPUB3 nav) + toc.ncx (EPUB2 readers), style.css, optional titlepage.xhtml, one reflowable XHTML chapter per document (XMLSerializer guarantees well-formed XML), data-URL images extracted to OEBPS/images/ (remote URLs kept as-is), manual page breaks → invisible markers.
    Page format for PDF comes from the project's page format (trade/pocket paperback etc. already supported).

ANDROID ONLY — FIXES
3. Atlas top bar scrolled away with the page

 Root cause: since the Atlas moved outside #app-root (v0.8.1 status-bar work) it renders at document level with no height-constrained ancestor. The shell was min-h-screen … overflow-y-auto: min-height lets the shell GROW with content, so its own overflow never activated — the document scrolled — while overflow-y-auto still made the sticky top-0 header bind to the shell's (never-scrolling) scrollport. Result: the whole page scrolled including the top bar (this also explains why it worked pre-iPad-update when Atlas lived inside the height-constrained #app-root).
 Fix (MundusAtlas.tsx + index.css): shell is now h-screen … overflow-hidden (+ height: 100dvh via @supports, matching the #app-root pattern); the utility header is a plain non-sticky flex child (permanently visible); everything below the header wrapped in <div class="flex-1 min-h-0 overflow-y-auto"> — only the content scrolls. The v0.8.2 .mundus-atlas-shell safe-area padding is kept → the bar sits exactly one status bar below the top (var max(--safe-area-inset-top, --mundus-safe-top)), never behind it. The obsolete sticky-top override rule was removed. Atlas only — no other view touched.

Refactors (behavior-identical, regression-covered)

 exportUtils.ts: buildFullPrintableHtml → thin wrapper over new exported buildPrintableHtmlShell(bodyHtml, title, options); exportToDocx → title block + new exported htmlToDocxParagraphs(html); exportToPdf page-render loop → new exported appendPaperCloneToPdf(pdf, clone, onProgress); buildPaperClone + PaperClone exported.
 App.tsx: added generic handleUpdateFolder(folderId, updates) (mirrors handleRenameFolder persistence) + wired onUpdateFolder / onUpdateProject into StartMenu.

Bugs found & fixed during development

 React error #310: the new bookLayout useMemo initially sat AFTER StartMenu's if (!isOpen) return null early return → hooks-count mismatch when opening. Moved above the early return.
 Mundus View display initially only used the resolved order in reorder mode; normal mode kept raw array order (post-reload rows appeared unsorted). Now both modes render the resolved order (filters applied on top in normal mode).

Files

  • New: src/lib/bookOrder.ts, src/lib/bookExport.ts, src/components/BookExportDialog.tsx, scripts/test_book_order.ts, scripts/e2e_v083.cjs
  • Modified: src/types.ts, src/components/StartMenu.tsx, src/App.tsx, src/components/MundusAtlas.tsx, src/index.css, src/lib/exportUtils.ts, package.json (+jszip), package-lock.json, src/constants.ts, index.html, electron/main.ts, android/app/build.gradle, public/sw.js

Full Changelog: v0.8.2...v0.8.3