fix: refresh magic notes after external updates

Subscribe the open Magic Notes workspace to committed database changes from desktop and Agent/MCP writes. Preserve selections and unsaved drafts during refresh and external deletion.

File-level commit includes existing runtime edits in shared files; their remaining dependencies are still in the working tree.

Release note: Magic Notes now updates automatically after Agent/MCP edits while preserving your selection and unsaved drafts.
This commit is contained in:
mesalogo
2026-09-15 00:31:21 +08:00
parent fbba2e3abf
commit e1fe1cf10a
19 changed files with 571 additions and 49 deletions
+6
View File
@@ -1604,6 +1604,12 @@ const desktopApi: DesktopApi = {
handler
)
},
onChanged: (listener) => {
const handler = (): void => listener()
ipcRenderer.on(ipcChannels.magicNotesChanged, handler)
return () =>
ipcRenderer.removeListener(ipcChannels.magicNotesChanged, handler)
},
onTodoStatusChanged: (listener) => {
const handler = (): void => listener()
ipcRenderer.on(ipcChannels.magicTodosStatusChanged, handler)
+25
View File
@@ -0,0 +1,25 @@
import { describe, expect, it, vi } from 'vitest'
import type { DesktopApi } from '../shared/contracts'
import { ipcChannels } from '../shared/ipc-channels'
const electron = vi.hoisted(() => ({
contextBridge: { exposeInMainWorld: vi.fn() },
ipcRenderer: { on: vi.fn(), removeListener: vi.fn() },
webUtils: {}
}))
vi.mock('electron', () => electron)
describe('Magic Notes preload events', () => {
it('forwards change notifications without exposing the Electron event and removes the same listener', async () => {
await import('./index')
const api = electron.contextBridge.exposeInMainWorld.mock.calls[0]![1] as DesktopApi
const listener = vi.fn()
const unsubscribe = api.magicNotes.onChanged(listener)
const [channel, handler] = electron.ipcRenderer.on.mock.calls[0]!
expect(channel).toBe(ipcChannels.magicNotesChanged)
handler({ sender: 'must stay in preload' })
expect(listener).toHaveBeenCalledExactlyOnceWith()
unsubscribe()
expect(electron.ipcRenderer.removeListener).toHaveBeenCalledExactlyOnceWith(channel, handler)
})
})