Skip to content

Move the open-project and pages-session logic out of the IPC files (#114) - #122

Merged
shovon merged 2 commits into
mainfrom
worktree-issue-114
Oct 28, 2026
Merged

shovon merged 2 commits into
mainfrom
worktree-issue-114

Conversation

@shovon

@shovon shovon commented Oct 28, 2026

Copy link
Copy Markdown
Owner

Closes #114.

What changed

Which project is open (app/src/main/features/projects/open-project/open-project.ts): openId, the fallback to the most recently opened project, requireProject and the "open project changed" callback move here from projects-ipc.ts, along with open(id) and remove(id). It takes a Registry of only the five store calls it uses, and imports nothing from electron.

The pages session (app/src/main/features/projects/pages/pages-session/pages-session.ts): the trail, the publish queue, the write queue (writes before, now write) and the Open type move here from pages-ipc.ts. It is handed the clock and a broadcast(state) instead of looping over BrowserWindow.getAllWindows() itself, and imports nothing from electron.

The IPC files are now only their channels. registerProjectIpc(store, openProject) and registerPageIpc(session) check arguments as they arrive and hand them on. What only Electron can do stays there: the directory picker, shell, the block menu, the image scheme, and broadcastPages, which sends to every window.

index.ts builds createOpenProject(store, () => pages.resetTrail()) and createPagesSession(open, now, broadcastPages) and passes them to the two register…Ipc calls. The wiring is otherwise unchanged.

app/CLAUDE.md points at the new files, and says that an IPC file is only its channels.

For the reviewer

  • One behaviour change: a write's arguments are now checked before it joins the queue, not in its turn. A malformed write (a bad block id, an untrimmed title) is refused at once instead of after the writes ahead of it. The renderer gets the same rejection. require is still called as the write arrives, so where a write lands is settled the same way as before.
  • A test from the issue that isn't here: "publish calls that overlap broadcast in the order they were asked". Each publish reads the trail and the state after its own await, so two that overlap send the same state, and no test can tell the order apart. The publish queue is unchanged. The write-order tests cover what it protects.
  • Found while testing, not caused by this: on a brand-new ledger, the first pages.show() reply reaches the renderer a few milliseconds before its pages:changed state, against the guarantee in PagesApi. It reproduces on the code from main too. That is being looked into separately.
  • The branch merges main after Write an AGENTS.md at the top of a ledger (#63) #121 (ledger: write an AGENTS.md at the root of a new ledger #63). It merged cleanly, including app/CLAUDE.md.

Tests

  • open-project.test.ts (9 tests, stub registry):
    • the fallback to the most recently opened project
    • opening an unknown id throws and moves nothing
    • the callback fires once per change and not when reopening the same project
    • removing the open project moves to the next most recent, and removing another one doesn't
    • an unreachable project is refused by name
    • requireProject settles which project at the moment it is called
  • pages-session.test.ts (7 tests, in-memory view over the real pagesFold, with a gate to hold writes in flight):
    • a write's state is sent before its reply
    • writes are recorded in arrival order, so one may name the block before it
    • a refused write doesn't hold up the next
    • a write lands on the project that was open when it was sent
    • the trail resets
    • nothing is sent when nothing is reachable
    • today follows the clock
  • Mutation checks: calling require in the write's turn instead of on arrival fails one test, and removing the write queue fails two.
  • End to end, in a scratch instance (its own --user-data-dir and scratch project folders), every pages:* and projects:* channel was called through window.pbnotes:
    • writes landed in order, and edit, indent and outdent worked
    • bad ids, bad titles and unknown blocks were refused
    • locate, search and status answered
    • the trail moved and reset when switching or removing projects
    • an image was stored and served back over pbnotes-image://
  • npm test (155 tests across all workspaces), npm run typecheck and npm run lint pass.

🤖 Generated with Claude Code

shovon and others added 2 commits October 27, 2026 17:12
)

Which project is open, the trail, and the two queues that order sends and
writes lived inside projects-ipc.ts and pages-ipc.ts, welded to ipcMain, so
none of it could run without Electron and none of it had a test.

open-project.ts now holds which project is open: the fallback to the most
recently opened one, requireProject, and the callback when the open project
changes, including when it is removed. It takes only the five registry calls
it uses. pages-session.ts holds the trail, the publish queue and the write
queue, and is handed the clock and a broadcast instead of reaching for
BrowserWindow. Neither imports electron.

The IPC files are now only their channels: arguments are checked as they
arrive and handed on. What only Electron can do stays there: the directory
picker, shell, the block menu, the image scheme, and sending to windows.
index.ts builds both and passes them in.

Arguments are now checked before a write joins the queue rather than in its
turn, so a malformed write is refused at once instead of after the writes
ahead of it. The renderer sees the same rejection.

The new tests cover the fallback, opening and removing, the reachability
check, that requireProject settles which project when called, that writes
are recorded in arrival order so one may name the block before it, that a
refused write does not hold up the next, that a write lands on the project
open when it was sent, and that each write's state is sent before its reply.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@shovon
shovon merged commit db6c78b into main Oct 28, 2026
1 check passed
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.

Move the open-project and pages-session logic out of the IPC files

1 participant