Preload and IPC

Finish renderer editing

Create, select, update, and delete notes in renderer state, then save through the preload API.

Now we finish the editor. The rule for this lesson: selection, form input, and unsaved changes all live in the renderer. Only persistence crosses the bridge, through window.notesAPI.saveNotes().

Create a note

A new note needs an ID. The browser can generate one for us with crypto.randomUUID(), so no library and no round trip to the main process:

function createNote() {
  const note = {
    id: crypto.randomUUID(),
    title: 'Untitled',
    body: ''
  }

  notes.unshift(note)
  selectedId = note.id
  renderNotes()
  showSelectedNote()
}

unshift puts the new note at the top of the list. We select it, redraw the list, and show it in the editor. showSelectedNote() is a small helper that copies the selected note’s title and body into the two form fields.

Save

When the form submits, copy the current field values into the selected note. Then send the whole array to the main process and wait:

async function save() {
  status.textContent = 'Saving…'

  try {
    const result = await window.notesAPI.saveNotes(notes)
    status.textContent = `Saved ${result.saved} notes`
  } catch {
    status.textContent = 'Could not save notes'
  }
}

Grab the status element next to the other references at the top of the file, with document.querySelector('#status').

The status goes to “Saving…” first, then to a success or a failure message. The main handler will return { saved: notes.length }, so the user sees how many notes were written.

Notice what we don’t do on failure: we don’t clear the editor. The unsaved note stays on screen so the user can retry, or at least copy the text somewhere. Losing a user’s writing because a disk write failed is the worst outcome an editor can have.

Delete and select

Deletion is the mirror of creation. Remove the note with the selected ID from notes, select the next available note, call renderNotes(), then call save() so the change reaches the disk.

Selection is selectNote(id): set selectedId, then showSelectedNote(). Always work with IDs here, never with array positions, for the reasons we saw when rendering the list.

Connect the menu event

Add one line at startup:

window.notesAPI.onNewNote(createNote)

Nothing happens yet. In a later lesson the native application menu sends notes:new, and this line is what makes “File → New Note” create a note.

Overlapping saves

Think about what happens if the user saves twice quickly. Two writes are in flight. The first one is slower, finishes last, and overwrites the newer data with the older version.

Two fixes work. Disable the Save button while a write is pending. Or keep a promise chain so each save starts after the previous one finishes. For Desktop Notes, disabling the button is enough.

Before you move on, exercise the failure path. Make the notes:save handler in the main process throw on purpose, then save a note. The status should change to “Could not save notes”, the note must stay editable, and no success message should appear before the promise settles. Then remove the throw.

Lesson completed