Skip to content

Releases

Mundus v0.8.65

Choose a tag to compare

@NotBedd NotBedd released this 28 Oct 17:40

v0.8.65 is a small update with a big impact!

Added support for 3 more language options for spellchecking and autocorrect:

  • Portuguese
  • Turkish
  • Russian

Full Changelog: v0.8.64...v0.8.65

Mundus v0.8.64

Choose a tag to compare

@NotBedd NotBedd released this 25 Oct 14:33

v0.8.64 Brings a few new features

Note 3: If you were having issues with a "bar" above your keyboard when on an android tablet, download the new apk hotfix below and update, it should solve the issue, sorry for not catching it earlier

Note 2: If updating your Android version from one earlier than v0.8.63, you'll need to first uninstall the old version then install this new one (this is one time only due to a signing change i needed to do). Before uninstalling the old version either backup the whole app or your individual Mundus projects or configure cloud sync, as uninstalling will delete your local files. Sorry for the inconvenience but I needed to do this, better get it out the way early right?

A toggle in the dictionary menu to disable autocorrect

  • Mostly done for mobile since autocorrect is built-in in keyboards already, it ended up as just annoying, could be appreciated by people who are amazing at typing already too (It's OFF on mobile by default and ON for desktop, it does NOT sync online as it would defeat the purpose)
Screenshot From 2026-10-25 14-44-57

Option in settings to use a new style for Map markers and upload custom icons

  • You can now upload custom icons to use in map markers instead of being limited to the built in ones (which will expand in the future)
  • You can now use a toggle in Settings to change to another style of map markers that uses a full sized icon in a circle instead of the pin head
  • Below are the toggle, the default markers style and the new marker style option in order (note the sword and quill icons that are not included in the built in ones)
Screenshot From 2026-10-25 14-45-10 Screenshot From 2026-10-25 14-45-53 Screenshot From 2026-10-25 14-46-00

Full Changelog: v0.8.63...v0.8.64

Full Changelog: v0.8.63...v0.8.64

Mundus v0.8.63

Choose a tag to compare

@NotBedd NotBedd released this 23 Oct 19:16

Mundus v0.8.63 brings together all the new features, improvements and fixes made between v0.8.4, v0.8.5, v0.8.6. v0.8.61 and v0.8.62

Here's a short summary to not clutter with multiple overwriting steps:

  • Note for android: Read the section at the bottom for two heads up about this and future releases

New Features

New full-book export system (DOCX tested on Word Online, PDF on Google PDF viewer and MJ PDF, EPUB on Moon Reader, different apps might give different results)

  • You can now reorder all your documents (and folders) in Mundus View to determine how they will be chained in the exported book
  • After reordering, you can either export the book as PDF, DOCX or EPUB
  • Optional toggles to include a title page, author tag and headings for each document based off of their name

Multi language support for spellchecking and autocorrect

  • Open the dictionary menu to change the spellchecking language, choose between English (default), Italian, Spanish, French and German
  • Autocorrect suggests based off the selected language (Note that Harper only works for English autocorrect, other languages fallback to a slightly less advanced system)
  • About the UI: I currently have no plan/timeline for a full UI translation, honestly I think the UI overall is easy enough that it being English probably isn't much of a problem, and that the core multi-lingual functionalities of spellchecking, autocorrect and Speech-to-text (Whistle supports various languages including all the ones above) is enough to make the app usable for projects in the mentioned languages, support for more may come in the future

DOCX export fixes

  • Before the docx export had various issues such as coloring any header-type text blue, not following page format margins and not embedding images correctly, all of these have been verified as working on Word Online, again other software might give different results

Android UI issues

When updating to this version (one time only, no more from now on) you'll have to uninstall the previous version before installing this new one, this is due to a change i had to make eventually and decided to just get out of the way. Backup all of your mundus before updating or just sync them to Firebase and re connect it in the new version

If after updating on phone your ui is stretched at the top of the screen and goes behind the status bar, close and reopen the app to fix it permanently, thiis a weird issue with some leftover UI process which I haven't yet figured out, but it's a very minor issue as simply reopening after the first time is enough

  • On phones the UI had various issues such as draft status buttons not being clickable, import / export buttons not being clickable and the top bar of the UI going behind Android's status bar or being stuck as a dark grey, all of these have been fixed
  • You can now access the auto save menu on phone by long pressing the save button

iPhone / iPad users

  • This version is also deployed on Netlify, follow the update routine to use it

Full Changelog: v0.8.3...v0.8.63

Mundus v0.8.3

Choose a tag to compare

@NotBedd NotBedd released this 22 Oct 20:08

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

Mundus v0.8.2

Choose a tag to compare

@NotBedd NotBedd released this 22 Oct 16:56

The app version jumped to v0.8 to signify the huge step done after publication

Below are the combined Worklogs of v0.8 , v0.8.1 and v0.8.2 (including fixes over previous implementations)

Mundus v0.8 - Work Log

1. Map Mode — custom marker icons actually render now

Root cause found: createMarkerIconHtml() in MapMode.tsx received the chosen iconName but never used it — every marker was drawn as the same teardrop pin with an empty white disc, which is why the icon picker "didn't really do much".

Fix: the chosen icon is now rendered as a real glyph (lucide SVG via renderToStaticMarkup) centered on the pin's white disc (14×14 px at the disc's center, dark stroke for contrast on any pin color). The default MapPin selection keeps the clean standard-pin look exactly as before. All icon sources work: the icon picked in the create/edit marker menu, category defaults, and icons inherited from Codex entries.

2. Harper suggests 3 corrections, cycle with arrows

  • lintToSuggestion() (grammarEngine.ts) now returns a choices list of up to 3 unique candidates (Harper's own precedence preserved; Remove-kind candidates map to "(delete)"; duplicates deduped).
  • While the suggestion popup is open, ← / → cycle through the candidates (editor caret movement is intercepted only while the popup is open, so normal navigation elsewhere is untouched). The popup shows ‹ › chevrons you can also click and a 1/3 counter.
  • Tab still confirms the currently shown candidate; Esc dismisses; "+ Add" whitelist unchanged. The dictionary fallback path also offers 3 choices now.

3. Sync status hover popup — new layout (expanded + collapsed variants)

Cloud Storage                0.02% of 1GB   ← percent keeps its dynamic color
[────────── bar ──────────]
This Mundus                        12.3 KB  ← single line, value on the right
All Mundus                          1.2 MB  ← single line, value on the right

The old bottom row crammed All Mundus: X + Quota: 1.0 GB together and wrapped; the quota moved up into the header (x% of 1GB), both bottom rows are now label-left/value-right and always fit on one line. Applied to both the expanded-sidebar tooltip and the collapsed-icon floating popup.

4. Highlighter: one-shot when applied to selected text

  • Selected text + color → the selection is painted, then the highlighter automatically disarms: the browser's typing background is cleared (scratch-collapse + hiliteColor transparent + selection restored) and a sync-guard prevents the toolbar state from re-latching from the just-painted text. Anything you type afterwards is not highlighted.
  • No selection (collapsed caret) + color → unchanged latching behavior: everything you type is highlighted until you toggle it off.

5. "BETA" badge removed from the Map button (sidebar)

Both the visible BETA chip and the title="Map (Beta)" tooltip are gone.

6. Renamed Codex categories now show in the "In This Scene" drawer

Entries store the section id (e.g. factions), but the drawer displayed the raw id — so renames never showed ("Factions" stuck forever). The drawer now receives the project's codexSections and displays the current label everywhere: header chips, entry badges (collapsed + expanded), category filter chips, and even search-by-category-name. Unknown/deleted ids fall back to the raw string like CodexMode does.

7. Scene Codex header cleaned up + Codex tab count

  • Removed the redundant "x codex entries detected in this scene" line under "Scene Codex".
  • The Codex tab now shows the number of detected codex entries (not characters): Codex (3) — matching the Characters (n) pattern of the tab next to it (tooltip explains the number).

8. Detection never matches inside words (xkylex / xkyle / kylex)

The drawer/editor/characters/map detectors were already whole-word aware, but the Codex entry form's "which documents mention this entry" list used raw substring matching (docText.includes(entryTitle)) — an entry titled "Kyle" was reported as mentioned by any document containing "xkylex". That logic is now a shared, whole-word helper getEntryDocumentMention() (sceneDetection.ts) built on the same Unicode-boundary matchesMention() engine: title in doc title → "In title", title in text → "Mentioned in text", subtitle (≥4 chars), tags (≥3 chars). Possessives ("Kyle's") still count; glued words never do.

9. Backups verified — deep/nested data round trip (test suite added)

New scripts/test_backup_verification.ts (38 checks) round-trips the REAL engine functions with a fixture containing every "deep" structure: character family tree (relatives), codex multi-image gallery (3 images incl. oversized data URLs), story flow link label + notes, story flow layouts, map markers with icons/colors, the full-res map image, document visual images, per-project calendars and renamed codex sections.

10. Android-only: long-press the Save button → Auto-Save dropdown

On Android (Capacitor), pressing and holding the manual Save button (500 ms, with a small haptic tick) opens the Auto-Save interval dropdown — on phones the compact toolbar hides the "Auto-Save:" button, so there was no touch way to reach it. A fired long-press suppresses the trailing tap so it never also triggers a manual save; text-selection/context-menu on long-press are suppressed. Desktop/PWA behavior is untouched (flag-gated by isAndroidApp()).

Mundus v0.8.1 — Work Log

NEW FEATURES / IMPROVEMENTS

A1. Settings toggle — Harper suggestions as a vertical list

  • New toggle in Settings → Editor & Scene Detection: "Show All Grammar Suggestions in a List" (stored in mundus_harper_vertical, applied live — no reload needed).
  • Off (default): unchanged horizontal popup with ‹ › cycling (←/→), Tab accepts.
  • On: all candidates (up to 3) render stacked vertically; the selected row is highlighted emerald with a check; ↑/↓ arrows move the selection (Tab still applies, click applies a row directly). Caret-arrow keys keep working normally in this mode since the popup no longer needs ←/→.

A2. DOCX import & export — full pass + verify loop

Discovery first: docx import (mammoth) and export already existed in the UI — but export was flat and lossy (no nested formatting, lists, colors, highlighters, images). What shipped:

  • Export rewritten (exportToDocx): recursive block walker — headings h1–h6, paragraphs, blockquotes, bullet lists (native docx bullets), numbered lists, nested inline formatting to any depth (bold/italic/underline/strikethrough), text colors (rgb/hex → docx hex), highlighter → real shading (w:shd fill), images (data-URL img → embedded media, type-detected png/jpg/gif/bmp, width-scaled), manual page breaks, zero-width-space scrubbing.
  • Verify loop (Playwright, headless Chromium, scripts/test_v081_docx_highlight.js) — 14 checks, all green:
    • docx export produces a valid file; title, H1, nested bold+italic, highlighter shading (w:shd), bullet list, numbered list, embedded image all verified by re-parsing the file with mammoth;
    • docx import roundtrip (own export → import): title + content + bold restored;
    • docx third-party import (a docx generated independently via the docx lib): H1, bold, paragraphs reconstructed;
    • PDF native roundtrip: export → re-import restores the document losslessly (title + content verified).
    • Bonus fix: hardened VisualImageOverlay's global click handler against non-Element targets (a latent target.closest crash on synthetic/edge events).

A3. Update-available indicator (green arrow)

  • New src/lib/updateCheck.ts: compares APP_VERSION against the latest published release of github.com/notbedd/mundus (GitHub releases API, unauthenticated). One check per app start, throttled by a 6-hour localStorage cache; offline/404/rate-limit = arrow simply stays hidden; a stale cache can never falsely claim an update (comparison is always recomputed against the running version).
  • New src/components/UpdateAvailableArrow.tsx: a green arrow chip next to the version badge in BOTH the Sidebar and the Atlas. Click opens the releases page (Electron routes it to the system browser via the existing window-open handler). Includes version-compare unit tests (semver-ish per-segment, v optional, malformed tags safe).

FIXES

F1. Storage space count — the 16 KB phantom

Root cause: Sidebar.calcBytes() added a hardcoded 1024 * 16 "base metadata overhead" to EVERY scope, and only documents+folders were measured.
Fix: new src/lib/storageEstimate.ts — REAL measurement, no constants:

  • per Mundus: JSON size (UTF-16, exactly how storage accounts bytes) of every record belonging to the project (documents, folders, characters, codex entries, timeline events, story links) + raw size of every per-project key (map markers, map image + name, world name, story-flow layout) + the project's slice of the config maps (codex sections, timeline/calendar) + per-document snapshots (resolved doc-id → owning project);
  • shared: every remaining mundus_* key (settings, dictionary, fonts…), with the global record lists and config maps excluded so nothing counts twice;
  • "All Mundus" = Σ(per-Mundus) + shared by construction — the rows now always reconcile; an empty Mundus shows its true (small) size; orphan keys of deleted worlds still count toward the total.
    Covered by 11 new unit tests (test_v081_helpers.ts), incl. the exact reconciliation scenario.

F2. Highlighter re-latch after selection-apply — FIXED (and verified in a real browser)

The v0.8.0 approach cleared the browser's typing style but couldn't stop DOM-adjacency inheritance: with the caret resting at the end of the freshly painted <span>, Chromium keeps inserting typed text INTO the styl...

Read more

Mundus v0.7.98

Choose a tag to compare

@NotBedd NotBedd released this 21 Oct 15:44

WORKLOG — Mundus v0.7.98 (v0.7.97 included)

  1. GitHub Actions build loop + everything it needs
  2. investigate & fix the "stuck at end of a page" scroll bug
  3. Character-name auto-highlight must not briefly disappear while the spelling check runs.
  4. Story flow graph: documents with draft status must show a grey dot (green/yellow for completed/in-review were reported as already correct).
  5. Sync status hover tooltip: show both the current Mundus's storage and the app-wide total (all Mundus combined).

1. GitHub build loop

  • .github/workflows/build.yml (new): tag trigger v* + workflow_dispatch; matrix windows-latest / ubuntu-latest / macos-latest; steps: checkout@v4 → setup-node@v4 (Node 22, npm cache) → npm ci → npm run build → npx electron-builder --publish never (targets already fully configured in electron-builder.json: NSIS+portable / AppImage+deb+rpm / dmg+zip) → artifacts per OS; release job (tag builds only) downloads artifacts and publishes a GitHub Release via softprops/action-gh-release@v2 with auto release notes. CSC_IDENTITY_AUTO_DISCOVERY=false (unsigned by design); permissions: contents: write.
  • .gitignore (rewritten): ignores node_modules/dist/dist-electron/release/coverage/tmp/logs/OS noise + secrets (.env*, firebase-applet-config.json — preserved from the original file). build/ deliberately kept tracked (icons + appstream required by electron-builder in CI — the original .gitignore wrongly ignored it).

2. Scroll-stuck bug — root cause + fix

Symptom: sometimes, at the end of a page, scrolling up bounces back down; re-clicking the document in the sidebar fixes it.

Root cause chain (all found in src/components/TextEditor.tsx):

  1. The manuscript scroller had onScroll={updatePageStats} — every scroll event ran the full pagination engine (normalizeDocumentPages).
  2. That engine mutates layout: strips every page-start block's virtual-gap margin-top (pre-pass), re-measures blocks (getBoundingClientRect / zoom-scale), re-applies huge gap margins on page-start blocks.
  3. Mutations while the viewport sits at a page end trigger Chrome scroll anchoring, which re-adjusts scrollTop toward the (moving) page-start block → the adjustment fires another scroll event → step 1 again → a mutation → anchoring → scroll feedback loop that traps the view at the page end. Intermittency explained: interior pages recompute stably (no mutation → no anchoring); boundary blocks can flip-flop on sub-pixel/rounding noise.
  4. A secondary amplifier: a ResizeObserver on the editor re-runs pagination whenever the pagination itself resizes the editor (boundary flip-flop ⇒ layout thrash).

Fix (3 layers):

  • A. onScroll no longer paginates — replaced with new updateCurrentPageFromScroll(): a read-only handler that refreshes the "page N of M" indicator from totalPagesRef + scrollTop (same math as the full pass, functional setPageStats that no-ops when unchanged). Pagination now happens only on input/save/resize/doc-switch/page-format paths, as intended.
  • B. #editor-scroll-area { overflow-anchor: none; } (index.css) — opts the scroller out of Chrome scroll anchoring entirely; any legitimate mid-scroll mutation (autosave, image insert) can no longer yank the viewport. No-op where unsupported (Safari).
  • C. Oscillation damper — normalizeDocumentPages now reports whether it actually mutated DOM (paginationMutatedRef); the ResizeObserver tracks a mutating-streak (4 passes within 800ms ⇒ 600ms cooldown). Direct input/save pagination bypasses the damper, so typing responsiveness is untouched.

3. Character highlight flicker during spellcheck — root cause + fix

Root cause (src/components/TextEditor.tsx, character-name highlight effect):

  • The effect's dependency-change cleanup deleted every registered highlight AND the shared <style> tag immediately, while re-application waited behind the existing 220 ms debounce. Every re-run therefore blacked the highlights out for ≥220 ms.
  • Re-run triggers in normal use: a keystroke (document.plainText), any pagination childList mutation (domStructureEpoch — bumped by the v0.7.83 observer after autosave/pagination), and character-card edits (characters). The spellcheck rescan (250 ms), squiggle re-render (~400 ms) and this blackout (0–220 ms) all land in the same post-typing window — which is why users perceived it as "the highlight disappears while the spelling check is being run".
  • Confirmed NOT the cause: currentMundusCharacters is useMemo-stable in App.tsx, so ordinary re-renders (syncStatus flips etc.) never re-triggered the effect — the blackout came purely from the nuke-then-debounce pattern.

Fix (atomic swap instead of nuke → debounce gap):

  • The dependency-change cleanup now only cancels the pending re-scan timer. Existing highlights stay painted (live Ranges keep tracking their text nodes through edits), and the debounced pass swaps old registrations for new ones inside a single synchronous task (delete + highlights.set + style update) — no paint can ever show the gap.
  • Real teardown moments keep immediate removal: feature toggle-off (branch), document switch (new charHighlightPrevDocIdRef check drops the previous document's registrations at once), and unmount (new dedicated useEffect(() => () => removeAllCharHighlightAssets(), [])).
  • Teardown logic hoisted to a module-level removeAllCharHighlightAssets() so the effect body and the unmount cleanup share one implementation.
  • Side benefit: highlights no longer blink during typing itself either (they used to vanish from the first keystroke until 220 ms after the last one).
  • Untouched on purpose: the spellcheck squiggle effect (its cleanup already never deleted spell-errors, which is why squiggles never flickered) and both debounce durations.

4. Status dots on the story flow graph (and timeline)

Root cause: the canonical status scheme lives in the editor's status picker (TextEditor.tsx): complete → emerald, revision ("in review") → amber, draft → neutral grey. The story-flow node dot only had a two-way mapping — doc.status === 'draft' ? 'bg-amber-400' : 'bg-emerald-400' — so drafts showed yellow and in-review scenes showed green (indistinguishable from completed).

Fix:

  • StoryFlowGraph.tsx node dot → three-way mapping matching the editor exactly: complete ? 'bg-emerald-500' : revision ? 'bg-amber-500' : 'bg-neutral-400' (dot size/title={doc.status} untouched; the now-unused isDraft const removed).
  • Same latent bug fixed in TimelineView.tsx (scene-timeline lane cards used the identical two-way mapping with rings) — kept the ring styling, now emerald-500/amber-500/neutral-400. (Beyond the letter of the request; same defect, fixed for consistency — flagging it explicitly.)

5. Storage tooltip — current Mundus + app-wide total

Root cause/finding: the sidebar's storage approximation only received documents/folders already filtered to the active Mundus (currentMundusDocs/currentMundusFolders in App.tsx). All projects actually share single storage keys (mundus_documents / mundus_folders, each row tagged mundusId), and App state holds the unfiltered lists — so the total was available without any extra reads.

Fix:

  • App.tsx passes two new optional props to : allDocuments={documents} and allFolders={folders} (no new storage scans, no async).
  • Sidebar.tsx recomputes both scopes with the same byte formula as before (content + plainText + title + 180 B/doc, +90 B/folder, +16 KB base): "This Mundus" keeps the per-project number; "All Mundus" sums every project. Props fall back to the scoped lists when omitted, so any other caller stays valid.
  • Tooltip (both the expanded-sidebar hover and the collapsed-sidebar portal copy) now reads:
    • header "Cloud Storage" + percent — percent now tracks the app-wide total (that's what counts against the Firestore free tier);
    • the progress bar is now live (width: storageBarPercent, clamped 1.5–100%) instead of the old hardcoded w-[1.5%];
    • rows: This Mundus: X / All Mundus: Y + Quota: 1.0 GB;
    • the offline (Firebase unconfigured) branch shows the same two rows.
  • Scope note (by design): the number stays the app's existing approximation of cloud usage (documents + folders + base overhead); characters/codex/timeline payloads and map images are not itemized, exactly as before — only the scope changed.

Full Changelog: v0.7.96...v0.7.98

Mundus v0.7.96

Choose a tag to compare

@NotBedd NotBedd released this 20 Oct 16:28

Mundus v0.7.96 — Bugfixing Release

Fixes (5)

1. Modern dialogs (no more native boxes)

  • NEW src/components/ConfirmDialog.tsx: promise-based in-app confirm host
    (showConfirmDialog({title, message, detail, confirmLabel, tone/danger}) → Promise).
    Styled like the rest of the app (rounded-2xl, border, backdrop blur, dark-mode aware,
    danger=red / warning=amber / info=accent), Enter confirms, Esc + backdrop click cancel,
    auto-focused confirm button, one-dialog-at-a-time. ConfirmDialogHost is mounted in BOTH
    app branches (Atlas + writing).
  • Replaced window.confirm for snapshot restore (App.tsx handleRestoreBackupSnapshot,
    now async) and for "Clear custom Firebase settings" (FirebaseConfigModal).
  • Replaced alert() for Firebase validation (inline red banner inside the modal instead of
    a blocking box) and for "+ Dictionary" in TextEditor (transient green "Added …" chip that
    outlives the button, which unmounts once the word joins the dictionary).
  • Bonus fix (latent React crash): FirebaseConfigModal returned null for closed state
    before its useState calls — hook count changed between renders. Hooks now run
    unconditionally; the form re-seeds via useEffect on open.
  • Swept the whole bundle: zero window.confirm/app-level alert( remain (the only alert(
    strings in the bundle are inside the third-party libwebp decoder).

2. Duplicate shortcut warning

  • src/lib/shortcutManager.ts: new bindingsEqual() + findBindingConflicts(bindings, excludeId, binding).
  • Settings → Keyboard Shortcuts:
    • Recording a combo that another action already uses shows an amber "Duplicate shortcut"
      banner (naming the conflicting action(s), auto-dismiss 8s, manual dismiss).
    • Persistent badges: any action whose combo is shared shows an amber chip + warning icon
      • "dup" tag until resolved; Reset Defaults clears everything.
    • Esc now cancels recording instead of binding Escape as a shortcut.
    • Shortcut groups lifted to module scope (KEYBINDING_GROUPS/KEYBINDING_LABELS) so the
      warning can name actions.

3. Settings submenus from the Atlas

  • Root cause: FirebaseConfigModal was rendered only in the writing-mode return. The Atlas
    is an early return, so "Configure Firebase" set the open state with no modal in the Atlas tree;
    it appeared only after opening a world.
  • Fix: extracted firebaseConfigModalElement and rendered it in both branches (same pattern
    the settings modal already used since v0.7.91). Verified E2E: Settings → Configure Firebase
    opens instantly on the Atlas.

4. Story Flow Graph layout survives backups

  • Root cause: the graph's layout (placed scene ids + x/y) lived only in localStorage
    (mundus_story_flow_nodes_ / mundus_story_flow_positions_). Backups carried story
    links but never the layout, so restoring on a fresh install/other device dropped every scene
    into default stacked positions; imports that renamed colliding ids also detached stored layouts.
  • NEW src/lib/storyFlowLayout.ts: readStoryFlowLayout, writeStoryFlowLayout,
    normalizeStoryFlowLayout (untrusted payloads), remapStoryFlowLayout (collision renames),
    mergeStoryFlowLayouts (backup wins for shared ids; local-only placed scenes kept — matches
    merge-import semantics), clearStoryFlowLayout, key helpers.
  • Backup payloads now carry layouts (storyFlowLayouts per-world map in app-wide backups +
    snapshots; storyFlowLayout in per-Mundus backups):
    • backupEngine.ts: ProjectBackupData, MundusBackupFile, export/build/parse for all three
      payload types extended (old backups without layouts restore harmlessly).
    • App.tsx: manual app-wide export, per-Mundus export, scheduled auto-download, and all three
      snapshot call sites (initial/timer/instant) capture layouts; app-wide import, snapshot
      restore, and Atlas import write them back (Atlas import remaps ids through the same rename
      table as items, then merges over the existing layout).
  • Deleting a world now also clears its two layout keys (v0.7.95 had cleaned map data; layouts
    were missed).

5. App-blanking crash (fixed)

  • TextEditor's status bar rendered document.wordCount.toLocaleString() /
    document.characterCount.toLocaleString() unguarded. A backup JSON missing characterCount
    (hand-edited/third-party imports) crashed React with no error boundary → blank screen on
    opening the world.
  • Fixed both ends: all five stat render sites guarded with || 0, StartMenu doc-row guarded,
    and applyMundusImport now derives missing wordCount/characterCount from plainText on
    the way in. E2E: the previously app-killing backup now imports and opens cleanly
    ("12 words · 13 chars" shown).

Files changed / added

New: src/components/ConfirmDialog.tsx, src/lib/storyFlowLayout.ts, scripts/test_v096_helpers.ts
Modified: src/App.tsx, src/components/SettingsModal.tsx, src/components/FirebaseConfigModal.tsx,
src/components/TextEditor.tsx, src/components/StartMenu.tsx, src/lib/backupEngine.ts,
src/lib/shortcutManager.ts, + the 6 version files.

Mundus v0.7.95

Mundus v0.7.95 Pre-release
Pre-release

Choose a tag to compare

@NotBedd NotBedd released this 20 Oct 13:07

Mundus v0.7.95 — Bug-Hunt & Hygiene Release

Fixes (8)

1. Deleting a character left their raw id displayed as a relative (the reported bug)

src/utils/characterRelativesSync.ts, src/App.tsx

  • Deleting a character (e.g. one of two partners) only filtered + tombstoned them; their id stayed inside every survivor's relatives (father/mother strings, siblings/partners/children arrays). Cards and the mention drawer then printed the id verbatim — "a number shown as partner".
  • New pure helper purgeDeletedCharacterReferences(survivors, deletedId, deletedName?) removes: (1) every ref equal to the deleted id, (2) opportunistically, any id-shaped (char-<digits>) ref that resolves to nobody — legacy dangling cleanup, (3) a ref equal to the deleted character's name only when no survivor owns that name (same-named survivors keep name refs meaningful).
  • handleDeleteCharacter now runs the purge, and cloud-saves each changed survivor (queueCharacterCloudSave) so the cleanup propagates. Local persistence happens via the existing debounced roster write.

2. CharacterCardDrawer printed raw relative ids (+ missing Siblings row)

src/components/characters/CharacterCardDrawer.tsx

  • The drawer (opened by clicking a character mention in the editor) rendered relatives raw — even existing character ids showed as char-1729…. It now resolves ids → names (same rule as the cards: id match, else case-insensitive name match, else shows a hand-typed custom name as-is).
  • Id-shaped refs resolving to nobody are hidden entirely (they can never be custom names), so pre-existing dangling data disappears from view without any risky migration.
  • The "Family & Lineage" box checked siblings in its header condition but never rendered a Siblings row — added.

3. CharactersMode cards hid nothing for dangling refs

src/components/characters/CharactersMode.tsx

  • Card relative rows + hasRelatives now use the same dangling-ref filter, so a card whose relatives are all dangling no longer shows an empty "Relatives" box with raw ids.

4. Local auto-snapshots couldn't restore Worlds

src/lib/backupEngine.ts, src/App.tsx

  • saveAutoBackupSnapshot omitted projects — the exact flaw v0.7.94 fixed for manual app-wide backups. Restoring a snapshot on a fresh/broken install left every document invisible (editor filters strictly by mundusId).
  • Snapshots now carry the world list; handleRestoreBackupSnapshot restores it with the same replace semantics + active-world validation ("mundus-prima" always valid, else newest restored world) as the manual path. All 3 snapshot call sites pass projects: mundusProjects.

5. Deleted worlds'/documents' content resurrected from the cloud (Firebase users)

src/App.tsx

  • handleDeleteDocument cleaned story links locally but never pushed them: the startup merge replaces the local link list from Firestore, so the deleted doc's links came back next launch. → now calls scheduleLoreCloudSave().
  • handleDeleteMundusProject cleaned local docs/folders/characters/entries/events/links but deleted only the project record in the cloud. Startup merge replaces local documents/folders from cloud and merges entries/characters back → the deleted world's content resurrected (docs/folders immediately; entries on merge; characters once the 7-day tombstone TTL pruned). Now cloud-deletes every scoped document, folder, codex entry and character (deleteUserDocument/Folder/CodexEntry/Character, fire-and-forget with catch) and pushes the cleaned events/links via scheduleLoreCloudSave().

6. Deleting a world orphaned its map data forever

src/App.tsx

  • mundus_map_markers_<id> / mundus_map_world_name_<id> in localStorage and the full-resolution map image in IndexedDB (32MB+ possible) were never removed on world deletion. Now removed (incl. deleteLocalMapImage).

7. Quota fallback freed nothing (wrong storage key)

src/App.tsx, src/lib/backupEngine.ts

  • The codex-entries quota fallback removed 'mundus_auto_backups' — a key that never exists (real key: 'mundus_local_auto_backups'), so the retry never freed space and entries still failed to save. AUTO_BACKUP_STORAGE_KEY is now exported and used everywhere (also fixes the phantom second key in persistCharactersMerged and hardcodes in handleClearSnapshots).

8. Unbalanced visibilitychange listener

src/App.tsx

  • The pagehide-flush effect registered visibilitychange inline and never removed it (the only add-without-remove in the file). Now a named handler removed in cleanup.

Files changed

  • src/App.tsx — fixes 1,4,5,6,7,8
  • src/utils/characterRelativesSync.ts — purge helper + looksLikeCharacterId (fix 1)
  • src/components/characters/CharacterCardDrawer.tsx — fix 2
  • src/components/characters/CharactersMode.tsx — fix 3
  • src/lib/backupEngine.ts — fix 4,7
  • scripts/test_relative_purge.ts — new suite (29 tests)
  • Version 0.7.95 / versionCode 788: package.json, package-lock.json (2), src/constants.ts, index.html (title+og), electron/main.ts, android/app/build.gradle