feat(notebook): add useAutomergeNotebook hook via runtimed-wasm#553
Merged
feat(notebook): add useAutomergeNotebook hook via runtimed-wasm#553
Conversation
…tend automerge Add the Tauri-side infrastructure for Phase 2 local-first migration: - `get_automerge_doc_bytes` command: exports the local Automerge doc as bytes so the frontend can initialize its own replica via Automerge.load() - `send_automerge_sync` command: receives raw sync messages from the frontend, applies them to the local doc, and relays to the daemon - Raw sync relay: spawns a task that forwards incoming daemon sync messages to the frontend via `automerge:from-daemon` events - `into_split_with_raw_sync`: new split variant that accepts an optional channel for raw sync byte forwarding - Frontend peer sync state tracking in the background task for proper bidirectional sync message generation The Tauri process keeps its Automerge replica (transitional) while also forwarding raw sync bytes for the frontend's future local document. Part of #540 Phase 2. https://claude.ai/code/session_01Vkb1BVso7Bh9TxHeegQwvW
The frontend_peer_state was initialized as sync::State::new() at run_sync_task startup. During cell population (before the frontend called GetDocBytes), daemon sync acks triggered generate_sync_message for the frontend, queuing stale messages in raw_sync_tx. When GetDocBytes later reset the state via virtual sync, those stale messages were already buffered. The frontend loaded the doc bytes, then applied the stale sync messages with a mismatched sync state, causing CRDT merges that produced phantom cells — cells present in the Automerge list (cell_count=2) but with unreadable IDs (available=1). Fix: start frontend_peer_state as None. Only initialize it inside GetDocBytes after the virtual sync handshake. Before that point, all generate_sync_message guards short-circuit on the None check, so no sync messages reach the frontend until its doc state is established. https://claude.ai/code/session_01Vkb1BVso7Bh9TxHeegQwvW
Phase 2 of the local-first Automerge migration. The frontend owns a local Automerge document via the runtimed-wasm NotebookHandle. All cell mutations (add, delete, edit source) execute instantly in WASM — no RPC round-trip. React state is derived from the doc. Sync messages flow through the Tauri relay to the daemon. Key changes: - useAutomergeNotebook: WASM-backed hook with identical interface to useNotebook - useNotebookDispatch: feature flag toggle (localStorage or ?automerge=true) - materialize-cells: extracted shared output resolution from useNotebook - App.tsx: imports from useNotebookDispatch instead of useNotebook directly The JS frontend never creates Automerge objects directly — all doc mutations go through NotebookHandle methods which execute the same Rust code as the daemon, guaranteeing schema compatibility (no string→Text CRDT mismatch). Feature flag OFF: existing useNotebook works unchanged. Feature flag ON: useAutomergeNotebook handles everything locally.
Member
Author
|
It's working and it's flying. One minor thing |
…e default Delete useNotebook.ts (legacy RPC+optimistic hook) and useNotebookDispatch.ts (feature flag toggle). App.tsx now imports useAutomergeNotebook directly.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase 2 of the local-first Automerge migration. The frontend owns a local Automerge document via the
runtimed-wasmNotebookHandle. All cell mutations (add, delete, edit source) execute instantly in WASM — no RPC round-trip. React state is derived from the doc. Sync messages flow through the Tauri relay to the daemon.What changed
useAutomergeNotebook— WASM-backed hook with identical interface touseNotebook. Bootstrap fromget_automerge_doc_bytes, mutations viaNotebookHandle, sync viaautomerge:from-daemonevents.useNotebookDispatch— Feature flag toggle.VITE_USE_AUTOMERGE=true cargo xtask dev, orlocalStorage.USE_AUTOMERGE_FRONTEND, or?automerge=true.materialize-cells— Extracted shared output resolution (blob manifests, stream merging) fromuseNotebookso both hooks use it.App.tsx— Imports fromuseNotebookDispatchinstead ofuseNotebookdirectly.Key constraint
The JS frontend never creates Automerge objects directly. All doc mutations go through
NotebookHandlemethods which execute the same Rust code as the daemon — no string→Text CRDT type mismatch.QA
Tested: cell CRUD, source sync, execution with outputs, ipywidgets (IntSlider with bidirectional comms), save-as with room reconnect, restart & run all (4 cells). Feature flag off: existing
useNotebookworks unchanged.Known limitation
Initial load shows empty notebook briefly while WASM initializes and doc bytes load. Optimization deferred — the daemon status banner covers the gap in practice.
Depends on #547's relay infrastructure (cherry-picked to
540/relay-cherry-pick).