Commit Graph
98 Commits
Author SHA1 Message Date
pj b63951f814 fix(library): surface unreadable books
list_books silently skipped any .margin file it could not read, parse,
or that lacked an id, so a truncated manuscript just vanished. Emit a
corrupt placeholder entry instead and show it in the library as a
non-openable card with a recovery hint pointing at the .bak backup.

Claude-Session: https://claude.ai/code/session_01RzTWrLiy4zGuw2hZSQGamk
2026-06-22 13:31:04 -04:00
pj aa75b3ebda fix(preview): destroy pdf.js document on recompile
The preview created a new PDF document on every recompile but never
destroyed the old one, leaking the document and its worker transport.
Keep the loading task and destroy it on effect cleanup.

Claude-Session: https://claude.ai/code/session_01RzTWrLiy4zGuw2hZSQGamk
2026-06-22 13:29:30 -04:00
pj 4e5ea6d855 fix(ui): handle library-open errors
Opening a book card swallowed failures: a corrupt or unreadable file
failed silently and normalizeBook could throw on a missing chapters
array. Surface load errors as a toast, throw a clear message on
unparseable JSON, and make normalizeBook tolerant of missing chapters,
metadata and settings.

Claude-Session: https://claude.ai/code/session_01RzTWrLiy4zGuw2hZSQGamk
2026-06-22 13:29:29 -04:00
pj 9eabef9041 feat(export): surface typst warnings and glyph-coverage notice
Typst compile warnings were discarded. Capture them via the Warned
result and emit a pdf-warnings event (only from the export path, not
the live preview) that App shows as a non-fatal toast; the export still
succeeds. Also scan book text before reporting success and warn when it
contains scripts the embedded Latin fonts cannot render (CJK, Arabic,
Hebrew, Devanagari, Thai, emoji).

Claude-Session: https://claude.ai/code/session_01RzTWrLiy4zGuw2hZSQGamk
2026-06-22 13:28:16 -04:00
pj 5c0040955e fix(export): correct list nesting, marks, line-start escaping, svg type
Four export-correctness fixes intermingled across the Typst and EPUB
renderers:

- H7: neutralize newlines in escaped text and apply leading-marker
  escaping (= - + / and N.) to the first text node of every block
  (paragraph, heading, list item, blockquote) and figure captions, even
  when that run carries a mark, so author text can never inject Typst
  markup or phantom headings/TOC entries.
- H9: render lists recursively so nested lists survive and multi-
  paragraph list items keep their paragraph breaks (Typst functional
  list()/enum(); EPUB nested <ul>/<ol> and multiple <p> in <li>).
- H10: render strike, underline and inline code marks in both exporters
  (Typst #strike/#underline/#raw; EPUB <s>/<u>/<code> with css).
- H8b: normalize +-bearing image subtypes so an imported svg+xml cover
  becomes cover.svg with the correct media type.

Validated the generated Typst constructs compile with typst 0.15.

Claude-Session: https://claude.ai/code/session_01RzTWrLiy4zGuw2hZSQGamk
2026-06-22 13:24:19 -04:00
pj 6e2ca9c4bc fix(persist): persist new books on create
A freshly created book lived only in memory until the first edit, so
quitting beforehand lost it and it never appeared in the library. Save
it to disk on creation via a shared createAndOpenBook used by both the
Library card and the New Book menu action.

Claude-Session: https://claude.ai/code/session_01RzTWrLiy4zGuw2hZSQGamk
2026-06-22 13:17:50 -04:00
pj 258d5e5209 fix(persist): guard markSaved against in-flight edits
markSaved cleared dirty unconditionally, so an edit made while a save
was in flight had its dirty flag wiped and its follow-up save dropped.
Only clear dirty when the book reference is unchanged since the save
started (immutable updates give a fresh reference per edit).

Claude-Session: https://claude.ai/code/session_01RzTWrLiy4zGuw2hZSQGamk
2026-06-22 13:17:50 -04:00
pj 14eb3c4427 fix(persist): flush pending save on navigate-away and quit
Edits are saved on an 800ms debounce, so leaving a book within that
window lost the latest changes. Flush a dirty book before openBook /
closeBook swap state (covers All books, open-another, Cmd+N), and add a
window close-request handler that saves before destroying on quit.

Claude-Session: https://claude.ai/code/session_01RzTWrLiy4zGuw2hZSQGamk
2026-06-22 13:16:33 -04:00
pj 0bc3e36ee6 feat(editor): persist active chapter per book
Remember which chapter was open per book and restore it on reopen,
falling back to the first chapter when the saved id is no longer valid.

Claude-Session: https://claude.ai/code/session_01RzTWrLiy4zGuw2hZSQGamk
2026-06-22 13:15:01 -04:00
pj c0da4c53db fix(editor): isolate undo history per chapter
Key the editor by chapter id so each chapter gets a fresh TipTap
instance with its own history. Previously a single instance was reused
across chapters, so the content swap entered the undo stack and Ctrl+Z
after switching restored the prior chapter's doc into the current one,
which autosave then persisted.

With remount, save the cursor/scroll position on unmount (from a ref,
not the editor, which is destroyed by then) and load it on mount,
replacing the in-place chapter-switch bookkeeping.

Claude-Session: https://claude.ai/code/session_01RzTWrLiy4zGuw2hZSQGamk
2026-06-22 13:13:12 -04:00
pj 92941a2758 fix(persist): atomic+durable .margin writes
Route save_book, write_file, write_bytes through a shared atomic_write
helper: write to a temp file, fsync, then rename into place so a crash
mid-write can never leave a truncated or empty manuscript. Rotate the
prior copy to .bak for manuscripts (not exports) so one good copy
always survives.

Claude-Session: https://claude.ai/code/session_01RzTWrLiy4zGuw2hZSQGamk
2026-06-22 13:12:39 -04:00
pj 61bd7cf6e5 changes 2026-06-22 13:03:11 -04:00
pj 72d698e427 changes 2026-06-22 12:40:29 -04:00
pj 8b83f80d52 fix(editor): persist per-chapter cursor and scroll across restarts
Positions were kept in an in-memory Map, so they were lost on quit and
the cursor jumped to the top on reopen. Store them in localStorage
(keyed by book + chapter, like theme/width) and restore on app open,
not just on in-session chapter switches. Also save the current chapter
continuously (debounced on selection + scroll) so closing the app
records where you left off, and re-apply scroll after fonts load.
2026-06-22 12:09:34 -04:00
pj 39609fbd09 perf(proofing): run proof_text on a blocking thread
proof_text loaded the dictionary and ran spell/grammar checks
synchronously inside an async command, blocking an async-runtime worker
and holding the engine mutex for the whole call. Move the work to
spawn_blocking so IPC stays responsive under load.
2026-06-22 12:01:25 -04:00
pj 3177b2f1eb fix(preview): show render errors instead of a blank pane
The render loop swallowed getDocument/render rejections, leaving the
preview silently blank with no signal about what failed. Surface the
error in the pane and recover on the next compile. Also drop the
redundant canvasContext arg (pdf.js v6 renders via the canvas itself).
2026-06-22 12:01:25 -04:00
pj 19c4823d3d changes 2026-06-22 11:56:31 -04:00
pj 5727592abe ci(release): rebase onto origin/main before pushing version bump 2026-06-22 11:48:49 -04:00
pj 706db33792 changes 2026-06-22 11:45:21 -04:00
pj bd44c0ba7e changes 2026-06-22 11:42:48 -04:00
pj 3c12d9857f action update 2026-06-22 10:26:30 -04:00
pj a9a4245de5 changes 2026-06-22 10:12:35 -04:00
pj 7cc36e3e40 changes 2026-06-22 09:56:20 -04:00
pj 78fff9b6f4 changes 2026-06-21 14:53:08 -04:00
pj fa1c89af63 changes 2026-06-21 14:37:02 -04:00
pj 47710970c2 changes 2026-06-21 14:17:57 -04:00
pj aa44696803 changes 2026-06-21 00:35:08 -04:00
pj 0e0d334f6a changes 2026-06-20 23:46:10 -04:00
pj c86d5a7d5c changes 2026-06-20 19:48:18 -04:00
pj 734ded73ac changes 2026-06-20 19:23:28 -04:00
pj 20ae0b7387 fix 2026-06-20 19:07:57 -04:00
pj be252c6b21 chore: remove in-code tests 2026-06-20 19:06:10 -04:00
pj 7464665d08 test(pdf): cover single-chapter preview compile 2026-06-20 19:03:46 -04:00
pj bfba86a01b perf(preview): typeset only the active chapter in live preview 2026-06-20 19:03:46 -04:00
pj 29e8842847 perf(preview): add per-chapter Typst compile inputs 2026-06-20 19:03:46 -04:00
pj 8b9a7081f7 feat(library): open example as a saved copy, hide it once a book exists 2026-06-20 18:53:36 -04:00
pj 4410b2037f refactor(sidebar): rename back button to All books 2026-06-20 18:46:43 -04:00
pj 9991d318ba feat(theme): persistent light/dark toggle in the titlebar 2026-06-20 18:46:43 -04:00
pj a5b3c0c0e3 feat(theme): monochrome black accent and light/dark surfaces 2026-06-20 18:46:43 -04:00
pj 79f3483b8e feat(epub): add Import EPUB entry to the home library 2026-06-20 18:41:13 -04:00
pj 3d3abd91f2 test(epub): cover unzip_epub reading a file from disk 2026-06-20 18:40:57 -04:00
pj 0d2d66ed6a fix(icons): restore dark-background app icon 2026-06-20 18:40:08 -04:00
pj 406cd1289c feat(epub): parse EPUB files into a Book (importer) 2026-06-20 18:36:18 -04:00
pj 8b9fa7f24e chore(window): blank the native window title 2026-06-20 18:33:43 -04:00
pj 847e912d05 refactor(library): remove brand logo and wordmark from home 2026-06-20 18:33:43 -04:00
pj 1cb738c20c fix(icons): ink mark on paper to match the logo (not inverted) 2026-06-20 18:30:53 -04:00
pj 18c367a82d chore(icons): generate app icon from the margin mark 2026-06-20 18:28:11 -04:00
pj 2997e8b79f feat(epub): add unzip_epub Tauri command for import 2026-06-20 18:24:09 -04:00
pj e09ce5cc92 chore(scripts): add pnpm dmg to bundle a macOS dmg 2026-06-20 18:20:22 -04:00
pj 11b6e81437 feat(app): use logo mark as favicon 2026-06-20 18:20:22 -04:00
pj 40d4c9f279 feat(library): show themed logo mark in the banner 2026-06-20 18:20:22 -04:00
pj 5e7b782f59 chore(assets): add margin logo mark 2026-06-20 18:20:22 -04:00
pj 041619821f feat(settings): draft Book setup, commit changes only on Save 2026-06-20 18:12:15 -04:00
pj 008e22c36c feat(export): apply print bleed and full-bleed full-page images 2026-06-20 18:12:15 -04:00
pj d684abbdef refactor(settings): drop trim field, now set from preview panel 2026-06-20 17:05:55 -04:00
pj 3202f24947 feat(preview): move trim size selector into the preview panel 2026-06-20 17:05:51 -04:00
pj 1d600ccc33 feat(menu): handle native menu actions in the app 2026-06-20 15:13:12 -04:00
pj f4a4d63349 feat(menu): native File menu with New Book and Export 2026-06-20 15:13:12 -04:00
pj f44e38d7fe style: library shelf, book cards, and setup save footer 2026-06-20 15:09:13 -04:00
pj e9669290ec feat(ui): back-to-library, export-only download button, save in book setup 2026-06-20 15:09:13 -04:00
pj 686c7768c7 refactor(app): route between library and editor with autosave 2026-06-20 15:09:13 -04:00
pj cd43f9c513 feat(library): book organizer home screen 2026-06-20 15:09:13 -04:00
pj 14d73df648 feat(library): list/load/save/delete library API 2026-06-20 15:09:13 -04:00
pj a6404847d1 refactor(store): nullable open book with open/close for library nav 2026-06-20 15:09:13 -04:00
pj 4896e10aba feat(model): give each book an id 2026-06-20 15:09:13 -04:00
pj 709dd46f1c feat(library): app-managed book storage commands 2026-06-20 15:09:13 -04:00
pj 7e7df12aa7 chore: add project guidelines 2026-06-20 14:47:30 -04:00
pj b1dac0ca71 fix(editor): remove floated drop cap that broke WebKit text input 2026-06-20 14:47:14 -04:00
pj 5e1582fc39 feat(ui): guard desktop-only actions with a clear browser-preview message 2026-06-20 14:30:07 -04:00
pj f8ddde5e91 fix(epub): embed fonts in the Rust packager instead of fetching in the webview 2026-06-20 14:30:07 -04:00
pj cb506af374 feat(ui): surface save and export errors as toasts; add webkit backdrop-filter 2026-06-20 13:29:52 -04:00
pj c4b69f3641 fix(editor): defer render and load content per chapter for WebKit 2026-06-20 13:28:25 -04:00
pj 0de442eab0 docs: project README 2026-06-20 13:09:40 -04:00
pj 3722d15594 feat(pdf): add title page and contents front matter 2026-06-20 13:08:10 -04:00
pj 0beac0c240 feat(ui): titlebar export menu and book-setup access 2026-06-20 13:02:30 -04:00
pj e424008d56 feat(export): PDF and EPUB export with save dialog 2026-06-20 13:02:30 -04:00
pj da94acc0ec feat(setup): book metadata and settings panel 2026-06-20 13:02:30 -04:00
pj 6daec6d203 fix(pdf): pass canvas to pdf.js render params 2026-06-20 13:02:30 -04:00
pj b1bd42fe2f feat(epub): add EPUB3 generator and zip packager 2026-06-20 13:02:30 -04:00
pj 4443eda401 feat(io): add write_bytes command for binary exports 2026-06-20 12:55:48 -04:00
pj aedf8b8a63 feat(io): save and open .margin projects with cmd-s and cmd-o 2026-06-20 12:54:01 -04:00
pj 13f2a70af2 feat(io): add read_file/write_file commands and dialog capability 2026-06-20 12:54:01 -04:00
pj eb63a6d49d chore(deps): add Tauri dialog plugin 2026-06-20 12:54:01 -04:00
pj d05480a714 style: figure, toolbar and pdf preview styles 2026-06-20 12:46:19 -04:00
pj 6769570de3 feat(pdf): wire live Typst PDF dock with HTML fallback 2026-06-20 12:46:19 -04:00
pj 29af847179 feat(export): serialize figures to Typst with image extraction 2026-06-20 12:46:18 -04:00
pj 6dc1aeb99b feat(editor): add floating formatting toolbar 2026-06-20 12:46:18 -04:00
pj 50bde45d65 feat(editor): add figure block with placement and caption 2026-06-20 12:46:18 -04:00
pj e76558fe1f feat(pdf): add pdf.js preview renderer 2026-06-20 12:42:04 -04:00
pj f939674794 feat(export): add book-to-Typst source generator 2026-06-20 12:42:04 -04:00
pj e21db2be5d feat(pdf): embed Typst compiler with compile_pdf command 2026-06-20 12:42:04 -04:00
pj 29c412849e feat(ui): connect shell to store with live preview dock 2026-06-20 12:28:44 -04:00
pj 3cf07d4ef6 feat(editor): add TipTap semantic editor 2026-06-20 12:28:43 -04:00
pj c76e5b0a5f feat(model): add document model and book store 2026-06-20 12:28:43 -04:00
pj 03a05b7e8f chore(deps): add TipTap and Zustand 2026-06-20 12:28:43 -04:00
pj dc88bf169f feat(ui): build app shell with chapters, editor and preview dock 2026-06-20 12:21:22 -04:00
pj 24483416a2 feat(design): add Quiet Press fonts and design tokens 2026-06-20 12:21:22 -04:00
pj 816e79a794 chore: scaffold Tauri + React + TS app 2026-06-20 12:21:22 -04:00