Skip to content

Feat/update node and other deps#102

Open
maagenta wants to merge 2 commits intoBoostIO:feat/update-node-and-other-depsfrom
maagenta:feat/update-node-and-other-deps
Open

Feat/update node and other deps#102
maagenta wants to merge 2 commits intoBoostIO:feat/update-node-and-other-depsfrom
maagenta:feat/update-node-and-other-deps

Conversation

@maagenta
Copy link
Copy Markdown
Contributor

@maagenta maagenta commented May 5, 2026

Fix IPC error.code loss and infinite render loop in MarkdownPreviewer

Summary

  • IPC error.code not preserved across Electron IPC: Electron does not
    preserve error.code when serializing exceptions through IPC, so catch
    blocks checking error.code === 'ENOENT' in prepareDirectory,
    loadBoostNoteJSON, and readdirOrEmpty always received undefined and
    re-threw instead of handling the error. Fixed by replacing the check with
    error.message?.includes('ENOENT') in electronOnly.ts, FSNoteDb.ts,
    and ImportLegacyNotesForm.tsx.

  • Infinite render loop in MarkdownPreviewer: remarkAdmonitionOptions
    and rehypeReactConfig were plain object literals recreated on every
    render, which invalidated markdownProcessor and renderContent on every
    render and kept firing the main useEffect in a loop. Fixed by wrapping
    both in useMemo, moving navigateToNote above rehypeReactConfig as a
    stable dependency, and removing renderedContent and rendering from the
    useEffect dependency array.

  • State update on unmounted component in Form: setSubmitState(false)
    was called inside a fire-and-forget Promise after await onSubmit(), which
    ran after the component unmounted when the submission closed its own modal.
    Fixed by awaiting onSubmit directly and guarding the state update behind
    a mountedRef.

Verified

  • Workspace that previously failed to load now initializes with defaults.
  • Markdown preview renders once with no loop in the console.
  • Creating a folder or workspace no longer triggers the "state update on unmounted component" warning.

maagenta and others added 2 commits May 5, 2026 03:09
…zation

PROBLEM
-------
After migrating to contextIsolation + contextBridge, file system IPC calls
broke for two related reasons:

1. Buffer → Uint8Array in contextBridge
   fs.promises.readFile() returns a Buffer in the main process. When passing
   through contextBridge, the structured clone algorithm serializes the Buffer
   as a Uint8Array. In the renderer, readFileAsString() called result.toString(),
   which on a Uint8Array inherits from Array.prototype.toString() and produces
   comma-separated byte values ("123,104,101,...") instead of the actual file
   content. JSON.parse() then failed with "Unexpected non-whitespace character
   after JSON at position 3" because the first number ("123") is valid JSON but
   the following comma is not.

   The previous commit (53d6b89) tried to work around this by adding a separate
   fs:read-text-file endpoint that read with 'utf8' encoding, avoiding the Buffer
   entirely. That fixed readFileAsString but left the root cause unaddressed and
   added unnecessary surface area to the IPC API.

2. error.code lost during IPC serialization (Electron 36+)
   When the main process threw an error (e.g. ENOENT from stat on a missing
   directory), Electron only preserves error.message when serializing exceptions
   across IPC, not error.code. The catch blocks in prepareDirectory,
   loadBoostNoteJSON and readdirOrEmpty were checking error.code === 'ENOENT',
   which was always undefined in the renderer, causing the error to be re-thrown
   instead of handled.

SOLUTION
--------
FSNoteDb.ts, ImportLegacyNotesForm.tsx, electronOnly.ts: simplifies the ENOENT
condition by replacing error.code === 'ENOENT' with error.message?.includes('ENOENT')
since error.code is not preserved when exceptions are serialized across IPC.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ate in Form

src/components/atoms/MarkdownPreviewer.tsx
  - Wrap remarkAdmonitionOptions and rehypeReactConfig in useMemo to prevent
    new object references on every render, which was causing markdownProcessor
    and renderContent to be recreated continuously and firing the main useEffect
    in a loop.
  - Move navigateToNote above rehypeReactConfig so it can be a stable dependency.
  - Remove renderedContent and rendering from the useEffect dependency array.

src/shared/components/molecules/Form/index.tsx
  - Guard setSubmitState(false) behind a mountedRef to prevent a state update
    on an unmounted component when the form submission closes its own modal.
  - Replace the fire-and-forget async Promise with a direct await.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant