@marimo-team/islands 0.21.2-dev57 → 0.21.2-dev59

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -80,6 +80,8 @@ export interface SetCellConfigRequest {
80
80
  export type UpdateUIElementRequest = schemas["UpdateUIElementRequest"];
81
81
  export type ModelRequest = schemas["ModelRequest"];
82
82
  export type UpdateCellIdsRequest = schemas["UpdateCellIdsRequest"];
83
+ export type NotebookDocumentTransactionRequest =
84
+ schemas["NotebookDocumentTransactionRequest"];
83
85
  export type UpdateUserConfigRequest = schemas["UpdateUserConfigRequest"];
84
86
  export type ShutdownSessionRequest = schemas["ShutdownSessionRequest"];
85
87
  export type Snippet = schemas["Snippet"];
@@ -138,6 +140,9 @@ export interface EditRequests {
138
140
  saveCellConfig: (request: SetCellConfigRequest) => Promise<null>;
139
141
  sendRestart: () => Promise<null>;
140
142
  syncCellIds: (request: UpdateCellIdsRequest) => Promise<null>;
143
+ sendDocumentTransaction: (
144
+ request: NotebookDocumentTransactionRequest,
145
+ ) => Promise<null>;
141
146
  sendInstallMissingPackages: (
142
147
  request: InstallPackagesRequest,
143
148
  ) => Promise<null>;
@@ -549,6 +549,7 @@ export class PyodideBridge implements RunRequests, EditRequests {
549
549
  };
550
550
 
551
551
  syncCellIds = () => Promise.resolve(null);
552
+ sendDocumentTransaction = () => Promise.resolve(null);
552
553
 
553
554
  addPackage: EditRequests["addPackage"] = async (request) => {
554
555
  return this.rpc.proxy.request.addPackage(request);
@@ -5,8 +5,12 @@ import { useRef } from "react";
5
5
  import { useErrorBoundary } from "react-error-boundary";
6
6
  import { toast } from "@/components/ui/use-toast";
7
7
  import { getNotebook, useCellActions } from "@/core/cells/cells";
8
+ import { applyTransactionChanges } from "@/core/cells/document-changes";
8
9
  import { AUTOCOMPLETER } from "@/core/codemirror/completion/Autocompleter";
9
- import type { NotificationPayload } from "@/core/kernel/messages";
10
+ import type {
11
+ NotificationMessageData,
12
+ NotificationPayload,
13
+ } from "@/core/kernel/messages";
10
14
  import { useConnectionTransport } from "@/core/websocket/useWebSocket";
11
15
  import { renderHTML } from "@/plugins/core/RenderHTML";
12
16
  import {
@@ -93,6 +97,17 @@ export function useMarimoKernelConnection(opts: {
93
97
  const { showBoundary } = useErrorBoundary();
94
98
 
95
99
  const { handleCellMessage, setCellCodes, setCellIds } = useCellActions();
100
+ const actionsWithoutMiddleware = useCellActions({ skipMiddleware: true });
101
+
102
+ const handleDocumentTransaction = (
103
+ transaction: NotificationMessageData<"notebook-document-transaction">["transaction"],
104
+ ) => {
105
+ applyTransactionChanges(
106
+ transaction.changes,
107
+ actionsWithoutMiddleware,
108
+ () => getNotebook().cellIds.inOrderIds,
109
+ );
110
+ };
96
111
  const { addCellNotification } = useRunsActions();
97
112
  const setKernelState = useSetAtom(kernelStateAtom);
98
113
  const setAppConfig = useSetAppConfig();
@@ -321,6 +336,9 @@ export function useMarimoKernelConnection(opts: {
321
336
  case "update-cell-ids":
322
337
  setCellIds({ cellIds: msg.data.cell_ids });
323
338
  return;
339
+ case "notebook-document-transaction":
340
+ handleDocumentTransaction(msg.data.transaction);
341
+ return;
324
342
  default:
325
343
  logNever(msg.data);
326
344
  }
@@ -27,6 +27,13 @@ type ReducerActions<RH extends ReducerHandlers<any>> = {
27
27
  : never;
28
28
  };
29
29
 
30
+ /** Helper for typing middleware that receives dispatched actions. */
31
+ export type DispatchedActionOf<T> = {
32
+ [Key in keyof T]: T[Key] extends (payload: infer P) => any
33
+ ? { type: Key; payload: P }
34
+ : never;
35
+ }[keyof T & string];
36
+
30
37
  export interface ReducerCreatorResult<
31
38
  State,
32
39
  RH extends ReducerHandlers<State>,
@@ -80,21 +87,20 @@ export function createReducerAndAtoms<
80
87
  State,
81
88
  RH extends ReducerHandlers<NoInfer<State>>,
82
89
  >(initialState: () => State, reducers: RH, middleware?: Middleware<State>[]) {
90
+ const allMiddleware = [...(middleware ?? [])];
91
+ const addMiddleware = (mw: Middleware<State>) => {
92
+ allMiddleware.push(mw);
93
+ };
83
94
  const { reducer, createActions } = createReducer(initialState, reducers);
84
95
 
85
96
  const reducerWithMiddleware = (state: State, action: ReducerAction<any>) => {
86
97
  try {
87
98
  const newState = reducer(state, action);
88
- if (middleware) {
89
- for (const mw of middleware) {
90
- try {
91
- mw(state, newState, action);
92
- } catch (error) {
93
- Logger.error(
94
- `Error in middleware for action ${action.type}:`,
95
- error,
96
- );
97
- }
99
+ for (const mw of allMiddleware) {
100
+ try {
101
+ mw(state, newState, action);
102
+ } catch (error) {
103
+ Logger.error(`Error in middleware for action ${action.type}:`, error);
98
104
  }
99
105
  }
100
106
  return newState;
@@ -108,9 +114,17 @@ export function createReducerAndAtoms<
108
114
  // map of SetAtom => Actions
109
115
  const actionsMap = new WeakMap();
110
116
 
111
- function useActions(): ReducerActions<RH> {
117
+ function useActions(
118
+ options: { skipMiddleware?: boolean } = {},
119
+ ): ReducerActions<RH> {
112
120
  const setState = useSetAtom(valueAtom);
113
121
 
122
+ if (options.skipMiddleware === true) {
123
+ return createActions((action: ReducerAction<any>) => {
124
+ setState((state: State) => reducer(state, action));
125
+ });
126
+ }
127
+
114
128
  if (!actionsMap.has(setState)) {
115
129
  actionsMap.set(
116
130
  setState,
@@ -126,6 +140,7 @@ export function createReducerAndAtoms<
126
140
 
127
141
  return {
128
142
  reducer: reducerWithMiddleware,
143
+ addMiddleware,
129
144
  createActions,
130
145
  valueAtom,
131
146
  useActions,