[nd 3/5] Frontend transaction sync#8844
Merged
manzt merged 3 commits intomanzt/nd-sessionfrom Mar 25, 2026
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
[nd 3/5] Frontend transaction sync
This was referenced Mar 24, 2026
ee2fc9f to
552dbb6
Compare
552dbb6 to
2bb42ff
Compare
2bb42ff to
468dd42
Compare
mscolnick
reviewed
Mar 24, 2026
| NotificationMessageData<"notebook-document-transaction">["transaction"]; | ||
| type TransactionOp = Transaction["ops"][number]; | ||
|
|
||
| interface CellActions { |
Contributor
There was a problem hiding this comment.
might be able to Pick<CellActions, ...> here.
Collaborator
Author
There was a problem hiding this comment.
I think i ran into some circular reference (because the type is inferred). We could probably have an explicit type somewhere that we reuse as a type-only import.
Contributor
There was a problem hiding this comment.
ah yes it is inferred. maybe we can make middleware additive
Contributor
manzt
commented
Mar 24, 2026
Comment on lines
+22
to
+36
| /** | ||
| * Actions the middleware intercepts. Payload types are hardcoded from | ||
| * the notebook reducer in cells.ts. A follow-up can infer these from | ||
| * the reducer directly once it's extracted to a named variable. | ||
| */ | ||
| type DocumentAction = | ||
| | { type: "createNewCell"; payload: { cellId: CellId } } | ||
| | { type: "deleteCell"; payload: { cellId: CellId } } | ||
| | { type: "moveCell"; payload: { cellId: CellId } } | ||
| | { type: "sendToTop"; payload: { cellId: CellId } } | ||
| | { type: "sendToBottom"; payload: { cellId: CellId } } | ||
| | { type: "dropCellOverCell"; payload: unknown } | ||
| | { type: "dropCellOverColumn"; payload: unknown } | ||
| | { type: "updateCellCode"; payload: { cellId: CellId; code: string } } | ||
| | { type: "updateCellName"; payload: { cellId: CellId; name: string } }; |
Collaborator
Author
There was a problem hiding this comment.
@mscolnick similar here... our middleware interface isn't strictly typed on the reducer. I hard-coded types to try to reduce the diff.
mscolnick
reviewed
Mar 24, 2026
| let cellId: CellId | "__end__" = "__end__"; | ||
| let before = false; | ||
| if (op.after) { | ||
| cellId = op.after as CellId; |
Contributor
There was a problem hiding this comment.
i think these as could be cleaned up now from your other PRs
468dd42 to
a766c32
Compare
ef3c69f to
6626bda
Compare
6626bda to
8f2f8bb
Compare
The document transaction middleware and apply-transaction logic were spread across two files with duplicated type definitions and several gaps in the op mapping. This PR consolidates them into `document-ops.ts` as two pure functions — `toDocumentOps` (action → ops) and `fromDocumentOps` (ops → actions) — with thin impure wrappers for debouncing and dispatch. The middleware was missing the `updateCellConfig` handler entirely (config changes were silently dropped), sent an empty config object for `createNewCell` instead of reading the actual `CellConfig` from post-reducer state, and produced `after: null` when a cell moved to the first position. Position derivation now uses an `anchorOf` helper that reads from `newState`, eliminating type casts and correctly handling `__end__`, column objects, and left/right moves without special-casing. Column structure actions (`addColumnBreakpoint`, `dropOverNewColumn`, etc.) previously only emitted `reorder-cells`, losing the per-cell column index. They now diff column assignments between prev and new state and emit `set-config` ops for cells whose column changed, keeping the Python `NotebookDocument` in sync. The middleware switch is exhaustive with `assertNever` — adding a new reducer action without handling it here is a compile error. A new round-trip test suite proves the two directions are correct inverses by performing actions on a primary notebook, applying the emitted ops to a replica, and asserting both converge to identical document state.
671e569 to
8002a4a
Compare
Prev #8842, #8843, #8844. This PR closes the kernel → session loop. **Transaction emission.** `_apply_ops` previously assembled three separate legacy notifications (`UpdateCellCodesNotification` grouped by stale status, plus `UpdateCellIdsNotification`). Now it builds typed `Op`s via `_plan_to_document_ops()` and broadcasts a **single** `NotebookDocumentTransactionNotification`. **Document context.** The execution endpoint snapshots `session.document.cells` into `ExecuteScratchpadCommand.notebook_cells`. The kernel handler sets a `ContextVar` from it so `AsyncCodeModeContext` can read cell state from the document.`_CellsView` now delegates entirely to the document and returns `NotebookCell` directly, removing the `NotebookCellData` wrapper and the module-level `_cell_names` dict.
manzt
added a commit
that referenced
this pull request
Mar 26, 2026
This PR introduces the core data model, with wiring to session (#8843), frontend (#8844), and kernel (#8845). `NotebookDocument` is an ordered list of `NotebookCell` entries that applies `Transaction`s atomically. A transaction is a tuple of typed ops (`CreateCell`, `DeleteCell`, `MoveCell`, `ReorderCells`, `SetCode`, `SetName`, `SetConfig`) with a `source` tag identifying the writer (e.g. `"frontend"`, `"kernel"`, `"file-watch"`). The document validates ops for conflicts (e.g. delete + update on the same cell) before applying. Version is `None` on creation and stamped by `apply()`. ```python doc = NotebookDocument([NotebookCell(id="a", code="x = 1", ...)]) tx = Transaction(ops=(SetCode(cell_id="a", code="x = 2"),), source="kernel") applied = doc.apply(tx) # stamps version → Transaction(version=1) ``` --------- Co-authored-by: Shahmir Varqha <Sham9871@gmail.com> Co-authored-by: Myles Scolnick <myles@marimo.io> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
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.

Prev #8842, #8843. This closes the frontend ↔ session loop in three steps:
Frontend → session. A reducer middleware intercepts cell actions (create, delete, move, rename, set-config) as the user performs them, batches them into transaction ops, debounces (400ms), and flushes to
POST /api/document/transaction. A suppress guard prevents echo loops when applying server-originated ops.Session → frontend.
applyTransactionOpsmaps each op to the corresponding cell reducer action.Queued-cell staleness. When the kernel queues a cell for execution, the frontend now snapshots
lastCodeRun. Previously only the user-initiated "Run" button did this, so kernel-initiated runs (e.g. via code_mode) left cells visually stale even after successful execution.