v0.1.15 update proved the pipeline end-to-end (download, verify, swap OK)
but the app never reopened: the handoff runs in a systemd transient unit
whose default KillMode=control-group sweeps the process tree when the
script exits, killing the backgrounded relaunch before Electron started.
Two fixes: start the unit with KillMode=process, and setsid the relaunch
(nohup fallback) so it detaches from the unit session entirely.
requestSingleInstanceLock() without a quit on failure means the second
process falls through to whenReady() and opens a duplicate window (what
Avi saw: two Folios from one icon click). Now the loser forwards to the
running window (second-instance) and exits. Pairs with the launcher
TMPDIR pin — uwsm per-scope /tmp made the singleton socket invisible, so
the lock never even failed before.
The in-app updater installs the release asset matched by pickAsset
(tar.gz + linux + x86_64/amd64); the config only built AppImage+deb, so
plain 'npm run package' produced no updatable asset and required a second
electron-builder invocation plus a manual rename (as done for 0.1.13).
Root cause of 0.1.8-0.1.12 shipping stale code: electron-builder packs
whatever dist/ happens to contain; dist was last built 2026-10-04 10:02,
so releases 0.1.8..0.1.12 all carried byte-identical bundles that predate
the systemd-run handoff fix (12977e1) and the curl downloader (9afe49c).
prepackage guarantees a fresh build on every npm run package.
Chromium's network stack restarts requests mid-stream on flaky Wi-Fi,
yielding a right-sized but corrupt gzip (progress hits 100% three times,
then 'archive corrupt'). curl resumes with HTTP Range so a dropped
connection picks up where it stopped, retries internally, and the whole
file is still byte-count + gunzip verified before handoff. Falls back to
the fetch pipeline when curl is absent.
On uwsm/Hyprland the app runs in a systemd scope cgroup; a detached child is
swept away when the app quits, which killed the swap mid-flight on every
in-app update attempt. systemd-run gives the installer its own transient
unit that outlives the scope; detached spawn stays as the non-systemd
fallback.
- installer script now logs every step (wait, extract, flatten, swap, relaunch)
to a persistent log so a failed update is diagnosable after the fact
- flatten the electron-builder nested dir by adopting the dir itself; the old
'mv NEW/* ' glob missed dotfiles and could leave a half-moved tree (prime
suspect for the two in-app update failures)
- mkdir failure aborts with relaunch of the old app instead of falling through
Migration recovery backups no longer re-copy Folio-Backups (up to 21x bloat). bookChangedSince tracks visited dev:ino so a symlink loop in the book cannot wedge the main-process interval tick. Snapshot toggle check aligned to === false; drop dead home-shortening branch in Settings.
English spellcheck suggestions with Add-to-dictionary in the native context menu plus a Settings toggle. Typewriter caret-centering mode. OS file associations for .md/.txt and Open File wording. Updater: release URL surfaced, single progress listener, two-byte tarball check, installer wait guard. Search/installer test suites registered; chapter-list layout polish; ignore KDE .directory files.
Background autosave toggle plus whole-book snapshots (default <book>/Folio-Backups, configurable location, retention, interval) with a one-minute change check in the main process. New Settings modal (sidebar, Ctrl+, , File menu) exposes autosave, snapshots, folder picker, and Snapshot now.
Keyboard reorder (Alt+Up/Down) restores focus and scrolls the moved row into view so repeated moves keep working. Pointer drags autoscroll near the list edges and show drop position.
Markdown:
- File > Open Markdown File... and welcome-screen 'Open .md' import
one or more .md files as new books (named after first H1 or the
filename; originals never modified, collision-safe folder names).
- Book dirs and .md files passed on the command line or via desktop
file associations now open directly; single-instance lock forwards
second launches to the running window.
Updates:
- File > Check for Updates... and welcome-screen 'Update' button:
queries the Gitea releases API, one-click installs a newer release
into ~/.local/share/Folio via a detached swap script that waits for
app exit, keeps the old tree until the swap succeeds, and relaunches.
Download is gzip-verified and restricted to the trusted release host.
No root, no reinstall.
Tests: 19 new assertions in tests/run-update-import-test.mjs (wired
into npm test); full suite green, tsc clean.
- add autoExportDocx() in src/main/index.ts:590 to refresh full-book and per-chapter .docx on every save
- hook saveChapterDoc, saveChapter, create/rename/delete/duplicate/restore/reorder/setColor to auto-generate DOCX in book folder
- primary autosave remains native JSON (folio-doc) for fidelity, DOCX is derived export
- fixes duplicate title in God is Decentralized chapter (remove H2 duplicate, chrome title is canonical)
- Add 'Save As...' item to native File menu, right under Save
- New IPC event folio:save-as-requested routes through preload to renderer
- Wired in index.ts to call saveChapterAs with current chapter ID
- Add 'Save As' button next to Save in the chapter writing view
- Exports current chapter as Markdown to a user-chosen file location
- New IPC handler folio:saveChapterAs shows OS save dialog
- Set .folio-prose.ProseMirror line-height from 1.7 to 1.0 to fix
excessive spacing between paragraphs and blockquotes
- Add PDF/DOCX/ODT export dropdown to the chapter writing view header,
matching the Full Book view's export workflow
- Wire chapter export dropdown toggle, menu selection, and close-on-outside-click