@marimo-team/islands 0.25.3-dev137 → 0.25.3-dev139

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.
Files changed (33) hide show
  1. package/dist/{ErrorBoundary-CgZ1IpNU.js → ErrorBoundary-BX9mTNLJ.js} +1 -1
  2. package/dist/assets/__vite-browser-external-DrmlAsRP.js +1 -0
  3. package/dist/assets/worker-O2XUgidQ.js +113 -0
  4. package/dist/{code-visibility-uIcGb1d8.js → code-visibility-gptWgGHp.js} +1 -1
  5. package/dist/{common-Cn-8v1pu.js → common-mU-2k6Zp.js} +1 -1
  6. package/dist/{data-grid-overlay-editor-CY38o06k.js → data-grid-overlay-editor-Bdyx_BTg.js} +1 -1
  7. package/dist/{feedback-button-C8MtB57f.js → feedback-button-BoXM4uir.js} +2 -2
  8. package/dist/{glide-data-editor-BK5sjGKi.js → glide-data-editor-DaXYMyzk.js} +3 -3
  9. package/dist/{globals-Dx5-Q1mU.js → globals-Bj4btPVp.js} +1 -1
  10. package/dist/main.js +29 -28
  11. package/dist/{number-overlay-editor-woxvu2w0.js → number-overlay-editor-DHfzwLIx.js} +1 -1
  12. package/dist/{reveal-component-BW15mD8s.js → reveal-component-CurCj3ks.js} +2 -2
  13. package/package.json +1 -1
  14. package/src/core/islands/__tests__/bootstrap.test.ts +17 -17
  15. package/src/core/islands/__tests__/bridge.test.ts +31 -26
  16. package/src/core/islands/bootstrap.ts +22 -51
  17. package/src/core/islands/bridge.ts +31 -20
  18. package/src/core/islands/worker/__tests__/controller.test.ts +0 -1
  19. package/src/core/islands/worker/controller.ts +0 -4
  20. package/src/core/islands/worker/worker.tsx +32 -32
  21. package/src/core/wasm/__tests__/bridge.test.ts +0 -1
  22. package/src/core/wasm/bridge.ts +0 -13
  23. package/src/core/wasm/rpc.ts +0 -1
  24. package/src/core/wasm/worker/bootstrap.ts +1 -15
  25. package/src/core/wasm/worker/types.ts +1 -6
  26. package/src/core/wasm/worker/worker.ts +4 -28
  27. package/src/core/websocket/__tests__/backend.test.ts +41 -13
  28. package/src/core/websocket/backend.ts +31 -7
  29. package/src/core/websocket/transports/__tests__/notebook.test.ts +1 -41
  30. package/src/core/websocket/transports/notebook.ts +0 -31
  31. package/dist/assets/__vite-browser-external-SaDYcg7x.js +0 -1
  32. package/dist/assets/worker-CfME3uyi.js +0 -115
  33. package/src/core/wasm/worker/message-buffer.ts +0 -33
package/dist/main.js CHANGED
@@ -7,7 +7,7 @@ import { C as T, F as E, G as D, I as O, J as k, K as A, L as j, P as M, R as N,
7
7
  import { a as oe, d as se, i as ce, m as le, n as ue, o as de, p as fe, r as pe, t as me } from "./tooltip-CJ4Rqasp.js";
8
8
  import { $ as he, A as ge, At as _e, B as ve, Bt as ye, D as be, Dt as xe, G as Se, H as Ce, Ht as we, It as Te, J as Ee, Jt as De, Kt as Oe, M as ke, Mt as Ae, N as je, Nt as Me, O as Ne, Ot as Pe, P as Fe, Q as Ie, S as Le, Tt as Re, V as ze, Vt as Be, Wt as Ve, X as He, Xt as Ue, Y as We, Yt as Ge, _ as Ke, _n as qe, _t as Je, an as Ye, b as Xe, cn as Ze, ct as Qe, dn as $e, dt as et, en as tt, f as nt, ft as rt, g as it, gn as at, gt as ot, h as st, it as ct, jt as lt, k as ut, kt as dt, ln as ft, m as pt, mn as mt, nn as ht, o as gt, p as _t, pn as vt, pt as yt, qt as bt, rn as xt, rt as St, s as Ct, tn as wt, u as Tt, un as Et, v as Dt, xt as Ot, y as kt, z as At } from "./error-entries-CmFDQ9CA.js";
9
9
  import { D as jt, E as Mt, O as Nt, S as Pt, T as Ft, _ as It, a as Lt, c as Rt, d as zt, f as Bt, g as Vt, h as Ht, i as Ut, l as Wt, m as Gt, n as Kt, o as qt, p as Jt, r as Yt, s as Xt, t as Zt, u as Qt, v as $t, w as en, x as tn, y as nn } from "./spec-DiSh_qJC.js";
10
- import { $t as rn, A as an, At as on, B as sn, Bt as cn, C as ln, Ct as un, D as dn, Dt as fn, E as pn, Et as mn, F as hn, Ft as gn, G as _n, Gt as vn, H as yn, Ht as bn, I as xn, It as Sn, J as Cn, Jt as wn, K as Tn, Kt as En, L as Dn, Lt as On, M as kn, Mt as An, N as jn, Nt as Mn, O as Nn, Ot as Pn, P as Fn, Pt as In, Qt as Ln, R as Rn, Rt as zn, S as Bn, St as Vn, T as Hn, Tt as Un, U as Wn, Ut as Gn, V as Kn, Vt as qn, W as Jn, Wt as Yn, X as Xn, Xt as Zn, Y as Qn, Yt as $n, Zt as er, _ as tr, _t as nr, an as rr, at as ir, b as ar, c as or, cn as sr, ct as cr, d as lr, dn as ur, dt as dr, en as fr, et as pr, f as mr, fn as hr, ft as gr, g as _r, gt as vr, h as yr, hn as br, ht as xr, in as Sr, it as Cr, j as wr, jt as Tr, k as Er, kt as Dr, ln as Or, lt as kr, m as Ar, mn as jr, mt as Mr, nn as Nr, nt as Pr, ot as Fr, p as Ir, pn as Lr, pt as Rr, q as zr, qt as Br, r as Vr, rn as Hr, rt as Ur, sn as Wr, st as Gr, t as Kr, tn as qr, tt as Jr, u as Yr, un as Xr, ut as Zr, v as Qr, vt as $r, w as ei, wt as ti, x as ni, y as ri, yt as ii, z as ai, zt as oi } from "./common-Cn-8v1pu.js";
10
+ import { $t as rn, A as an, At as on, B as sn, Bt as cn, C as ln, Ct as un, D as dn, Dt as fn, E as pn, Et as mn, F as hn, Ft as gn, G as _n, Gt as vn, H as yn, Ht as bn, I as xn, It as Sn, J as Cn, Jt as wn, K as Tn, Kt as En, L as Dn, Lt as On, M as kn, Mt as An, N as jn, Nt as Mn, O as Nn, Ot as Pn, P as Fn, Pt as In, Qt as Ln, R as Rn, Rt as zn, S as Bn, St as Vn, T as Hn, Tt as Un, U as Wn, Ut as Gn, V as Kn, Vt as qn, W as Jn, Wt as Yn, X as Xn, Xt as Zn, Y as Qn, Yt as $n, Zt as er, _ as tr, _t as nr, an as rr, at as ir, b as ar, c as or, cn as sr, ct as cr, d as lr, dn as ur, dt as dr, en as fr, et as pr, f as mr, fn as hr, ft as gr, g as _r, gt as vr, h as yr, hn as br, ht as xr, in as Sr, it as Cr, j as wr, jt as Tr, k as Er, kt as Dr, ln as Or, lt as kr, m as Ar, mn as jr, mt as Mr, nn as Nr, nt as Pr, ot as Fr, p as Ir, pn as Lr, pt as Rr, q as zr, qt as Br, r as Vr, rn as Hr, rt as Ur, sn as Wr, st as Gr, t as Kr, tn as qr, tt as Jr, u as Yr, un as Xr, ut as Zr, v as Qr, vt as $r, w as ei, wt as ti, x as ni, y as ri, yt as ii, z as ai, zt as oi } from "./common-mU-2k6Zp.js";
11
11
  import { c as si, i as ci, n as li, o as ui, t as di } from "./useLifecycle-B5BX921H.js";
12
12
  import { t as fi } from "./check-SpOMoCLP.js";
13
13
  import { A as pi, C as mi, D as hi, E as gi, M as _i, O as vi, S as yi, T as bi, a as xi, b as Si, c as Ci, i as wi, l as Ti, n as Ei, o as Di, r as Oi, s as ki, t as Ai, w as ji, x as Mi } from "./select-CkF_GBxO.js";
@@ -15,8 +15,8 @@ import { B as Ni, C as Pi, J as Fi, Ot as Ii, S as Li, W as Ri, X as zi, Y as Bi
15
15
  import { C as fa, _ as pa, a as ma, b as ha, c as ga, f as _a, h as va, m as ya, p as ba, s as xa, u as Sa, x as Ca, y as wa } from "./toDate-DGvRHwTk.js";
16
16
  import { a as Ta, c as Ea, d as Da, f as Oa, i as ka, l as Aa, n as ja, o as Ma, r as Na, s as Pa, t as Fa } from "./process-output-Bw-vzyoy.js";
17
17
  import { a as Ia, c as La, d as Ra, i as za, l as Ba, r as Va, u as Ha } from "./glide-utils-Dz_F4pAY.js";
18
- import { n as Ua, t as Wa } from "./ErrorBoundary-CgZ1IpNU.js";
19
- import { n as Ga, t as Ka } from "./globals-Dx5-Q1mU.js";
18
+ import { n as Ua, t as Wa } from "./ErrorBoundary-BX9mTNLJ.js";
19
+ import { n as Ga, t as Ka } from "./globals-Bj4btPVp.js";
20
20
  import { t as qa } from "./react-dom-DUmH0GXv.js";
21
21
  import { t as Ja } from "./jsx-runtime-3BD1kT0s.js";
22
22
  import { B as Ya, C as H, Ct as Xa, D as Za, Dt as Qa, E as $a, Et as eo, F as U, G as to, H as no, I as ro, L as io, M as W, N as ao, O as oo, P as so, Q as co, R as lo, S as G, St as uo, Tt as fo, U as po, W as mo, Y as ho, Z as go, _ as _o, _t as vo, a as yo, b as bo, bt as xo, c as So, d as Co, f as wo, g as To, h as Eo, i as Do, j as K, k as Oo, l as ko, m as Ao, n as jo, o as Mo, p as No, r as Po, s as Fo, u as Io, ut as Lo, v as Ro, w as zo, wt as Bo, x as Vo, xt as Ho, y as Uo, yt as Wo } from "./use-restore-focus-CmGwRNId.js";
@@ -39,7 +39,7 @@ import { t as Ps } from "./useAsyncData-BmVgnMP5.js";
39
39
  import { i as Fs, n as Is, r as Ls } from "./formats-CCxaLPNs.js";
40
40
  import { D as Rs, O as zs } from "./vega-loader.browser-B9Pz0EGP.js";
41
41
  import { a as Bs, n as Vs } from "./loader-DOVB0Syl.js";
42
- import { c as Hs, d as Us, i as Ws, l as Gs, o as Ks, s as qs, t as Js, u as Ys } from "./code-visibility-uIcGb1d8.js";
42
+ import { c as Hs, d as Us, i as Ws, l as Gs, o as Ks, s as qs, t as Js, u as Ys } from "./code-visibility-gptWgGHp.js";
43
43
  import { t as Xs } from "./useDeepCompareMemoize-Ch_AeGu6.js";
44
44
  import { t as Zs } from "./esm-D2gTsXEV.js";
45
45
  //#region ../node_modules/.pnpm/iconify-icon@2.3.0/node_modules/iconify-icon/dist/iconify-icon.mjs
@@ -10635,7 +10635,7 @@ function WC(e, t) {
10635
10635
  }
10636
10636
  //#endregion
10637
10637
  //#region src/plugins/impl/DataEditorPlugin.tsx
10638
- var GC = J.lazy(() => import("./glide-data-editor-BK5sjGKi.js").then((e) => e.t)), KC = DS("marimo-data-editor", { cssStyles: [XS] }).withData(W({
10638
+ var GC = J.lazy(() => import("./glide-data-editor-DaXYMyzk.js").then((e) => e.t)), KC = DS("marimo-data-editor", { cssStyles: [XS] }).withData(W({
10639
10639
  initialValue: W({ edits: G(W({
10640
10640
  rowIdx: K(),
10641
10641
  columnId: U(),
@@ -30407,7 +30407,7 @@ function WR(e) {
30407
30407
  }
30408
30408
  //#endregion
30409
30409
  //#region src/components/editor/renderers/slides-layout/slides-layout.tsx
30410
- var GR = J.lazy(() => import("./reveal-component-BW15mD8s.js")), KR = (e) => {
30410
+ var GR = J.lazy(() => import("./reveal-component-CurCj3ks.js")), KR = (e) => {
30411
30411
  let t = (0, X.c)(33), { layout: n, setLayout: r, cells: i, mode: a } = e, o = is(pa), s = a === "read" || o, c = is(Ke) > 1, [l, u] = (0, J.useState)(null), d;
30412
30412
  t[0] !== i || t[1] !== n ? (d = HP(i, n), t[0] = i, t[1] = n, t[2] = d) : d = t[2];
30413
30413
  let { slideCells: f, skippedIds: p, noOutputIds: m, slideTypes: h, startCellIndex: g } = d, _;
@@ -32115,7 +32115,7 @@ async function RB(e) {
32115
32115
  LB();
32116
32116
  for (let e of r) e.style.removeProperty("opacity");
32117
32117
  }
32118
- }), r.consumeMessages(({ message: e, sessionGeneration: n }) => {
32118
+ }), r.consumeNotifications((e, n) => {
32119
32119
  zB(e, a, t, n);
32120
32120
  }), document.addEventListener(Ta.TYPE, () => {
32121
32121
  t.set(Us, !0);
@@ -32123,8 +32123,7 @@ async function RB(e) {
32123
32123
  }
32124
32124
  function zB(e, t, n, r) {
32125
32125
  try {
32126
- let i = Ue(e);
32127
- switch (c.debug("Islands received message:", i.op), i.op) {
32126
+ switch (c.debug("Islands received message:", e.op), e.op) {
32128
32127
  case "banner":
32129
32128
  case "missing-package-alert":
32130
32129
  case "environment-operation":
@@ -32163,7 +32162,7 @@ function zB(e, t, n, r) {
32163
32162
  });
32164
32163
  return;
32165
32164
  case "kernel-ready":
32166
- tB(i.data, {
32165
+ tB(e, {
32167
32166
  autoInstantiate: !0,
32168
32167
  setCells: t.setCells,
32169
32168
  setLayoutData: a.NOOP,
@@ -32174,43 +32173,43 @@ function zB(e, t, n, r) {
32174
32173
  }), window.customElements?.get(bu.ISLAND) || w_(bu.ISLAND, NB);
32175
32174
  return;
32176
32175
  case "send-ui-element-message":
32177
- y_.broadcastMessage(i.data.ui_element, i.data.message, M(i.data));
32176
+ y_.broadcastMessage(e.ui_element, e.message, M(e));
32178
32177
  return;
32179
32178
  case "remove-ui-elements":
32180
- nB(i.data);
32179
+ nB(e);
32181
32180
  return;
32182
32181
  case "function-call-result":
32183
- b_.resolve(i.data.function_call_id, i.data);
32182
+ b_.resolve(e.function_call_id, e);
32184
32183
  return;
32185
32184
  case "cell-op":
32186
- rB(i.data, t.handleCellMessage);
32185
+ rB(e, t.handleCellMessage);
32187
32186
  return;
32188
32187
  case "alert":
32189
32188
  x({
32190
- title: i.data.title,
32191
- description: E({ html: i.data.description }),
32192
- variant: i.data.variant
32189
+ title: e.title,
32190
+ description: E({ html: e.description }),
32191
+ variant: e.variant
32193
32192
  });
32194
32193
  return;
32195
32194
  case "query-params-append":
32196
- iB.append(i.data);
32195
+ iB.append(e);
32197
32196
  return;
32198
32197
  case "query-params-set":
32199
- iB.set(i.data);
32198
+ iB.set(e);
32200
32199
  return;
32201
32200
  case "query-params-delete":
32202
- iB.delete(i.data);
32201
+ iB.delete(e);
32203
32202
  return;
32204
32203
  case "query-params-clear":
32205
32204
  iB.clear();
32206
32205
  return;
32207
32206
  case "model-lifecycle":
32208
- qg(Kg, i.data);
32207
+ qg(Kg, e);
32209
32208
  return;
32210
32209
  case "consumer-capabilities":
32211
32210
  case "execution": return;
32212
32211
  default:
32213
- mi(i);
32212
+ mi(e);
32214
32213
  return;
32215
32214
  }
32216
32215
  } catch (e) {
@@ -32445,7 +32444,7 @@ function QB(e, t) {
32445
32444
  }
32446
32445
  //#endregion
32447
32446
  //#region src/core/islands/worker/worker.tsx?worker&url
32448
- var $B = "/assets/worker-CfME3uyi.js", eV = class {
32447
+ var $B = "/assets/worker-O2XUgidQ.js", eV = class {
32449
32448
  url;
32450
32449
  name;
32451
32450
  constructor(e = {}) {
@@ -32465,7 +32464,7 @@ function tV(e) {
32465
32464
  //#region src/core/islands/bridge.ts
32466
32465
  var nV = class {
32467
32466
  rpc;
32468
- messageConsumer;
32467
+ notificationConsumer;
32469
32468
  store;
32470
32469
  root;
32471
32470
  session;
@@ -32491,8 +32490,10 @@ var nV = class {
32491
32490
  this.store.set(Hs, !0), this.initialized.resolve();
32492
32491
  }), this.rpc.addMessageListener("initializedError", ({ error: e }) => {
32493
32492
  c.error("Islands initialization error:", e), this.store.set(Hs, e), this.initialized.reject(Error(e));
32494
- }), this.rpc.addMessageListener("kernelMessage", (e) => {
32495
- this.activeSessionGenerations.has(e.sessionGeneration) && this.messageConsumer?.(e);
32493
+ }), this.rpc.addMessageListener("notebookEvent", ({ event: e, sessionGeneration: t }) => {
32494
+ if (!this.activeSessionGenerations.has(t)) return;
32495
+ let n = Ue(e);
32496
+ n.event === "marimo" && n.data.marimo && this.notificationConsumer?.(n.data.marimo, t);
32496
32497
  });
32497
32498
  }
32498
32499
  async initializeApps() {
@@ -32545,8 +32546,8 @@ var nV = class {
32545
32546
  async startSession(e) {
32546
32547
  await this.rpc.proxy.request.startSession(e);
32547
32548
  }
32548
- consumeMessages(e) {
32549
- this.messageConsumer = e, this.rpc.proxy.send.consumerReady({});
32549
+ consumeNotifications(e) {
32550
+ this.notificationConsumer = e;
32550
32551
  }
32551
32552
  sendComponentValues = async (e) => (await this.putControlRequest({
32552
32553
  type: "update-ui-element",
@@ -1,6 +1,6 @@
1
1
  import { a as e } from "./rolldown-runtime-Cb76FRpK.js";
2
2
  import { t } from "./react-sHtZdjeP.js";
3
- import { a as n } from "./glide-data-editor-BK5sjGKi.js";
3
+ import { a as n } from "./glide-data-editor-DaXYMyzk.js";
4
4
  //#region ../node_modules/.pnpm/@glideapps+glide-data-grid@6.0.4-alpha24_lodash@4.17.21_marked@15.0.12_react-dom@19.3.0_27cffe7f1bc30287d59ac27db3240c5b/node_modules/@glideapps/glide-data-grid/dist/esm/internal/data-grid-overlay-editor/private/number-overlay-editor-style.js
5
5
  var r = /* @__PURE__ */ e(t()), i = /*#__PURE__*/ n("div")({
6
6
  name: "NumberOverlayEditorStyle",
@@ -4,14 +4,14 @@ import { t as o } from "./react-sHtZdjeP.js";
4
4
  import { t as s } from "./compiler-runtime-CldRdSsv.js";
5
5
  import { m as c, t as l } from "./tooltip-CJ4Rqasp.js";
6
6
  import { k as u } from "./error-entries-CmFDQ9CA.js";
7
- import { $ as d, Q as f, Z as p, an as m, i as h, l as g, ln as _, n as v, on as y, r as b, t as x, u as S } from "./common-Cn-8v1pu.js";
7
+ import { $ as d, Q as f, Z as p, an as m, i as h, l as g, ln as _, n as v, on as y, r as b, t as x, u as S } from "./common-mU-2k6Zp.js";
8
8
  import { M as C } from "./select-CkF_GBxO.js";
9
9
  import { J as ee } from "./dropdown-menu-GWcGCcxE.js";
10
10
  import { _ as w } from "./toDate-DGvRHwTk.js";
11
11
  import { t as T } from "./jsx-runtime-3BD1kT0s.js";
12
12
  import { q as E } from "./use-restore-focus-CmGwRNId.js";
13
13
  import { A as D, C as O, b as k } from "./useTheme-Bq9pi_vz.js";
14
- import { a as A, n as te, t as ne } from "./code-visibility-uIcGb1d8.js";
14
+ import { a as A, n as te, t as ne } from "./code-visibility-gptWgGHp.js";
15
15
  /* empty css */
16
16
  var j = c("sticky-note", [["path", {
17
17
  d: "M21 9a2.4 2.4 0 0 0-.706-1.706l-3.588-3.588A2.4 2.4 0 0 0 15 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2z",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marimo-team/islands",
3
- "version": "0.25.3-dev137",
3
+ "version": "0.25.3-dev139",
4
4
  "main": "dist/main.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -6,17 +6,17 @@ import { RuntimeState } from "@/core/kernel/RuntimeState";
6
6
  import { initializeIslands } from "../bootstrap";
7
7
  import type { IslandsPyodideBridge } from "../bridge";
8
8
  import { islandsHydratedAtom, islandsPendingInitialRunsAtom } from "../state";
9
- import type { IslandsKernelMessage } from "../worker/worker";
10
-
11
- function completedRun(sessionGeneration: number): IslandsKernelMessage {
12
- return {
13
- sessionGeneration,
14
- message: JSON.stringify({
15
- op: "completed-run",
16
- data: { op: "completed-run", run_id: null },
17
- }) as IslandsKernelMessage["message"],
18
- };
19
- }
9
+ import type { NotificationMessage } from "@/core/kernel/messages";
10
+
11
+ type Consumer = (
12
+ notification: NotificationMessage,
13
+ sessionGeneration: number,
14
+ ) => void;
15
+
16
+ const completedRun = {
17
+ op: "completed-run",
18
+ run_id: null,
19
+ } as NotificationMessage;
20
20
 
21
21
  describe("initializeIslands", () => {
22
22
  afterEach(vi.restoreAllMocks);
@@ -26,9 +26,9 @@ describe("initializeIslands", () => {
26
26
  root.innerHTML =
27
27
  '<marimo-island data-app-id="app-1" data-reactive="true"></marimo-island>';
28
28
 
29
- let consumeMessage: ((message: IslandsKernelMessage) => void) | undefined;
29
+ let consumeMessage: Consumer | undefined;
30
30
  const bridge = {
31
- consumeMessages: (consumer: (message: IslandsKernelMessage) => void) => {
31
+ consumeNotifications: (consumer: Consumer) => {
32
32
  consumeMessage = consumer;
33
33
  },
34
34
  sendComponentValues: vi.fn(),
@@ -48,14 +48,14 @@ describe("initializeIslands", () => {
48
48
  expect(store.get(islandsHydratedAtom)).toBe(false);
49
49
  store.set(islandsPendingInitialRunsAtom, new Set([1, 2]));
50
50
 
51
- consumeMessage?.(completedRun(1));
51
+ consumeMessage?.(completedRun, 1);
52
52
  expect(store.get(islandsHydratedAtom)).toBe(false);
53
53
 
54
- consumeMessage?.(completedRun(1));
55
- consumeMessage?.(completedRun(0));
54
+ consumeMessage?.(completedRun, 1);
55
+ consumeMessage?.(completedRun, 0);
56
56
  expect(store.get(islandsHydratedAtom)).toBe(false);
57
57
 
58
- consumeMessage?.(completedRun(2));
58
+ consumeMessage?.(completedRun, 2);
59
59
  expect(store.get(islandsHydratedAtom)).toBe(true);
60
60
  });
61
61
  });
@@ -10,7 +10,6 @@ import {
10
10
  } from "@/__tests__/branded";
11
11
  import { notebookAtom } from "@/core/cells/cells";
12
12
  import { islandsPendingInitialRunsAtom } from "@/core/islands/state";
13
- import type { IslandsKernelMessage } from "../worker/worker";
14
13
 
15
14
  type Base64String = components["schemas"]["Base64String"];
16
15
  interface TestIslandApp {
@@ -89,9 +88,6 @@ vi.mock("@/core/wasm/rpc", () => ({
89
88
  startSession: mockStartSessionRequest,
90
89
  stopSession: mockStopSessionRequest,
91
90
  },
92
- send: {
93
- consumerReady: vi.fn(),
94
- },
95
91
  },
96
92
  addMessageListener: vi.fn(
97
93
  (name: string, listener: (payload: never) => void) => {
@@ -177,17 +173,22 @@ describe("IslandsPyodideBridge", () => {
177
173
  });
178
174
  }
179
175
 
180
- function sendKernelMessage(sessionGeneration: number) {
181
- const listener = mockMessageListeners.get("kernelMessage");
176
+ function sendNotebookEvent(sessionGeneration: number, frame: unknown) {
177
+ const listener = mockMessageListeners.get("notebookEvent");
182
178
  if (!listener) {
183
- throw new Error("Missing kernel message listener");
179
+ throw new Error("Missing notebook event listener");
184
180
  }
185
- const message = {
186
- message: "" as IslandsKernelMessage["message"],
187
- sessionGeneration,
188
- };
189
- listener(message as never);
190
- return message;
181
+ listener({ event: JSON.stringify(frame), sessionGeneration } as never);
182
+ }
183
+
184
+ /** Send a notification and return the call the consumer should receive. */
185
+ function sendKernelMessage(sessionGeneration: number) {
186
+ const notification = { op: "interrupted" };
187
+ sendNotebookEvent(sessionGeneration, {
188
+ event: "marimo",
189
+ data: { marimo: notification },
190
+ });
191
+ return [notification, sessionGeneration];
191
192
  }
192
193
 
193
194
  async function initializeSingleApp() {
@@ -300,7 +301,7 @@ describe("IslandsPyodideBridge", () => {
300
301
  describe("app lifecycle", () => {
301
302
  it("ignores messages after a session yields ownership", async () => {
302
303
  const consumeMessage = vi.fn();
303
- bridge.consumeMessages(consumeMessage);
304
+ bridge.consumeNotifications(consumeMessage);
304
305
  mockSingleApp();
305
306
  await bridge.initializeApps();
306
307
 
@@ -319,10 +320,7 @@ describe("IslandsPyodideBridge", () => {
319
320
  sendKernelMessage(1);
320
321
  const currentMessage = sendKernelMessage(2);
321
322
 
322
- expect(consumeMessage.mock.calls).toEqual([
323
- [firstMessage],
324
- [currentMessage],
325
- ]);
323
+ expect(consumeMessage.mock.calls).toEqual([firstMessage, currentMessage]);
326
324
 
327
325
  finishReplacement();
328
326
  await replacement;
@@ -330,7 +328,7 @@ describe("IslandsPyodideBridge", () => {
330
328
 
331
329
  it("restores message ownership when replacement fails", async () => {
332
330
  const consumeMessage = vi.fn();
333
- bridge.consumeMessages(consumeMessage);
331
+ bridge.consumeNotifications(consumeMessage);
334
332
  mockSingleApp();
335
333
  await bridge.initializeApps();
336
334
  mockReplaceSessionRequest.mockRejectedValueOnce(
@@ -344,12 +342,12 @@ describe("IslandsPyodideBridge", () => {
344
342
  const previousMessage = sendKernelMessage(1);
345
343
  sendKernelMessage(2);
346
344
 
347
- expect(consumeMessage.mock.calls).toEqual([[previousMessage]]);
345
+ expect(consumeMessage.mock.calls).toEqual([previousMessage]);
348
346
  });
349
347
 
350
348
  it("ignores messages while their session is stopping", async () => {
351
349
  const consumeMessage = vi.fn();
352
- bridge.consumeMessages(consumeMessage);
350
+ bridge.consumeNotifications(consumeMessage);
353
351
  mockSingleApp();
354
352
  await bridge.initializeApps();
355
353
  let finishStop!: () => void;
@@ -373,7 +371,7 @@ describe("IslandsPyodideBridge", () => {
373
371
 
374
372
  it("forwards messages from every app in a multi-app document", async () => {
375
373
  const consumeMessage = vi.fn();
376
- bridge.consumeMessages(consumeMessage);
374
+ bridge.consumeNotifications(consumeMessage);
377
375
  mockParseMarimoIslandApps.mockReturnValue([app(), app("app-2")]);
378
376
  mockCreateMarimoFile
379
377
  .mockReturnValueOnce("generated app 1")
@@ -385,10 +383,17 @@ describe("IslandsPyodideBridge", () => {
385
383
  const secondMessage = sendKernelMessage(2);
386
384
  sendKernelMessage(3);
387
385
 
388
- expect(consumeMessage.mock.calls).toEqual([
389
- [firstMessage],
390
- [secondMessage],
391
- ]);
386
+ expect(consumeMessage.mock.calls).toEqual([firstMessage, secondMessage]);
387
+ });
388
+
389
+ it("delivers only the notebook stream's marimo notifications", async () => {
390
+ const consumeMessage = vi.fn();
391
+ bridge.consumeNotifications(consumeMessage);
392
+ await initializeSingleApp();
393
+ sendNotebookEvent(1, { event: "ready", id: "3", data: {} });
394
+ const notification = sendKernelMessage(1);
395
+
396
+ expect(consumeMessage.mock.calls).toEqual([notification]);
392
397
  });
393
398
 
394
399
  it("holds controls until the first app session is ready", async () => {
@@ -9,11 +9,7 @@ import {
9
9
  } from "@/plugins/impl/anywidget/registry";
10
10
  import { initializePlugins } from "@/plugins/plugins";
11
11
  import { Functions } from "@/utils/functions";
12
- import {
13
- safeExtractSetUIElementMessageBuffers,
14
- type JsonString,
15
- } from "@/utils/json/base64";
16
- import { jsonParseWithSpecialChar } from "@/utils/json/json-parser";
12
+ import { safeExtractSetUIElementMessageBuffers } from "@/utils/json/base64";
17
13
  import { Logger } from "@/utils/Logger";
18
14
  import { logNever } from "@/utils/assertNever";
19
15
  import {
@@ -30,10 +26,7 @@ import {
30
26
  handleKernelReady,
31
27
  handleRemoveUIElements,
32
28
  } from "../kernel/handlers";
33
- import type {
34
- NotificationMessage,
35
- NotificationMessageType,
36
- } from "../kernel/messages";
29
+ import type { NotificationMessage } from "../kernel/messages";
37
30
  import { queryParamHandlers } from "../kernel/queryParamHandlers";
38
31
  import { RuntimeState } from "../kernel/RuntimeState";
39
32
  import { initialModeAtom } from "../mode";
@@ -126,19 +119,9 @@ export async function initializeIslands(
126
119
  }
127
120
  });
128
121
 
129
- // Wire up kernel message handling
130
- // The wire format is {"op": "...", "data": {"op": "...", ...}} — both
131
- // the envelope and the payload carry the op. The bridge types the message
132
- // as NotificationPayload (just {data}), but the actual wire format
133
- // includes the outer op too.
134
- // Worker metadata identifies the session that emitted the message.
135
- bridge.consumeMessages(({ message, sessionGeneration }) => {
136
- handleMessage(
137
- message as unknown as JsonString<IslandsNotificationMessage>,
138
- actions,
139
- store,
140
- sessionGeneration,
141
- );
122
+ // Each notification names the session that sent it.
123
+ bridge.consumeNotifications((notification, sessionGeneration) => {
124
+ handleNotification(notification, actions, store, sessionGeneration);
142
125
  });
143
126
 
144
127
  // Track first user interaction before initialization completes
@@ -154,27 +137,15 @@ export async function initializeIslands(
154
137
  RuntimeState.INSTANCE.start(bridge.sendComponentValues);
155
138
  }
156
139
 
157
- type IslandsNotificationMessage = {
158
- [K in NotificationMessageType]: {
159
- data: Extract<NotificationMessage, { op: K }>;
160
- op: K;
161
- };
162
- }[NotificationMessageType];
163
-
164
- /**
165
- * Handles a single message from the kernel.
166
- *
167
- * Wire format from Python: {"op": "<name>", "data": {"op": "<name>", ...}}
168
- */
140
+ /** Handles a single notification from the kernel. */
169
141
  // oxlint-disable-next-line marimo/prefer-object-params
170
- function handleMessage(
171
- message: JsonString<IslandsNotificationMessage>,
142
+ function handleNotification(
143
+ msg: NotificationMessage,
172
144
  actions: NotebookActions,
173
145
  store: Store,
174
146
  sessionGeneration: number,
175
147
  ): void {
176
148
  try {
177
- const msg = jsonParseWithSpecialChar(message);
178
149
  Logger.debug("Islands received message:", msg.op);
179
150
 
180
151
  switch (msg.op) {
@@ -223,7 +194,7 @@ function handleMessage(
223
194
  return;
224
195
 
225
196
  case "kernel-ready":
226
- handleKernelReady(msg.data, {
197
+ handleKernelReady(msg, {
227
198
  autoInstantiate: true,
228
199
  setCells: actions.setCells,
229
200
  setLayoutData: Functions.NOOP,
@@ -239,47 +210,47 @@ function handleMessage(
239
210
 
240
211
  case "send-ui-element-message":
241
212
  UI_ELEMENT_REGISTRY.broadcastMessage(
242
- msg.data.ui_element,
243
- msg.data.message,
244
- safeExtractSetUIElementMessageBuffers(msg.data),
213
+ msg.ui_element,
214
+ msg.message,
215
+ safeExtractSetUIElementMessageBuffers(msg),
245
216
  );
246
217
  return;
247
218
 
248
219
  case "remove-ui-elements":
249
- handleRemoveUIElements(msg.data);
220
+ handleRemoveUIElements(msg);
250
221
  return;
251
222
 
252
223
  case "function-call-result":
253
- FUNCTIONS_REGISTRY.resolve(msg.data.function_call_id, msg.data);
224
+ FUNCTIONS_REGISTRY.resolve(msg.function_call_id, msg);
254
225
  return;
255
226
 
256
227
  case "cell-op":
257
- handleCellNotificationeration(msg.data, actions.handleCellMessage);
228
+ handleCellNotificationeration(msg, actions.handleCellMessage);
258
229
  return;
259
230
 
260
231
  case "alert":
261
232
  toast({
262
- title: msg.data.title,
263
- description: renderHTML({ html: msg.data.description }),
264
- variant: msg.data.variant,
233
+ title: msg.title,
234
+ description: renderHTML({ html: msg.description }),
235
+ variant: msg.variant,
265
236
  });
266
237
  return;
267
238
 
268
239
  case "query-params-append":
269
- queryParamHandlers.append(msg.data);
240
+ queryParamHandlers.append(msg);
270
241
  return;
271
242
  case "query-params-set":
272
- queryParamHandlers.set(msg.data);
243
+ queryParamHandlers.set(msg);
273
244
  return;
274
245
  case "query-params-delete":
275
- queryParamHandlers.delete(msg.data);
246
+ queryParamHandlers.delete(msg);
276
247
  return;
277
248
  case "query-params-clear":
278
249
  queryParamHandlers.clear();
279
250
  return;
280
251
 
281
252
  case "model-lifecycle":
282
- handleWidgetMessage(WIDGET_REGISTRY, msg.data);
253
+ handleWidgetMessage(WIDGET_REGISTRY, msg);
283
254
  return;
284
255
 
285
256
  case "consumer-capabilities":
@@ -5,15 +5,16 @@ import { getWorkerRPC } from "@/core/wasm/rpc";
5
5
  import { Deferred } from "@/utils/Deferred";
6
6
  import { throwNotImplemented } from "@/utils/functions";
7
7
  import { Logger } from "@/utils/Logger";
8
+ import { jsonParseWithSpecialChar } from "@/utils/json/json-parser";
8
9
  import { generateUUID } from "@/utils/uuid";
9
10
  import { initialNotebookState, notebookAtom } from "../cells/cells";
10
- import type { ClientCommand } from "../kernel/messages";
11
+ import type { ClientCommand, NotificationMessage } from "../kernel/messages";
11
12
  import type { EditRequests, RunRequests } from "../network/types";
12
13
  import { store as defaultStore } from "../state/jotai";
13
14
  import { getMarimoExportContext } from "../static/export-context";
14
15
  import { createMarimoFile, parseMarimoIslandApps } from "./parse";
15
16
  import { islandsInitializedAtom, islandsPendingInitialRunsAtom } from "./state";
16
- import type { IslandsKernelMessage, WorkerSchema } from "./worker/worker";
17
+ import type { WorkerSchema } from "./worker/worker";
17
18
  import type { WorkerFactory } from "./worker-factory";
18
19
  import { DefaultWorkerFactory } from "./worker-factory";
19
20
 
@@ -52,14 +53,14 @@ interface AppSession {
52
53
  * @example
53
54
  * ```ts
54
55
  * const bridge = new IslandsPyodideBridge();
55
- * bridge.consumeMessages(message => console.log(message));
56
+ * bridge.consumeNotifications((notification) => console.log(notification.op));
56
57
  * await bridge.initializeApps();
57
58
  * ```
58
59
  */
59
60
  export class IslandsPyodideBridge implements RunRequests, EditRequests {
60
61
  private rpc: ReturnType<typeof getWorkerRPC<WorkerSchema>>;
61
- private messageConsumer:
62
- | ((message: IslandsKernelMessage) => void)
62
+ private notificationConsumer:
63
+ | ((notification: NotificationMessage, sessionGeneration: number) => void)
63
64
  | undefined;
64
65
  private readonly store: typeof defaultStore;
65
66
  private readonly root: Document | Element;
@@ -110,14 +111,24 @@ export class IslandsPyodideBridge implements RunRequests, EditRequests {
110
111
  },
111
112
  );
112
113
 
113
- this.rpc.addMessageListener("kernelMessage", (message) => {
114
- // A stopped session can still have messages in transit after the next
115
- // app takes ownership of the shared frontend state.
116
- if (!this.activeSessionGenerations.has(message.sessionGeneration)) {
117
- return;
118
- }
119
- this.messageConsumer?.(message);
120
- });
114
+ this.rpc.addMessageListener(
115
+ "notebookEvent",
116
+ ({ event, sessionGeneration }) => {
117
+ // A stopped session can still have events in transit after the next
118
+ // app takes ownership of the shared frontend state.
119
+ if (!this.activeSessionGenerations.has(sessionGeneration)) {
120
+ return;
121
+ }
122
+ // Islands follow only the notebook's marimo notifications.
123
+ const frame = jsonParseWithSpecialChar(event) as {
124
+ event: string;
125
+ data: { marimo?: NotificationMessage };
126
+ };
127
+ if (frame.event === "marimo" && frame.data.marimo) {
128
+ this.notificationConsumer?.(frame.data.marimo, sessionGeneration);
129
+ }
130
+ },
131
+ );
121
132
  }
122
133
 
123
134
  public async initializeApps(): Promise<void> {
@@ -246,14 +257,14 @@ export class IslandsPyodideBridge implements RunRequests, EditRequests {
246
257
  await this.rpc.proxy.request.startSession(opts);
247
258
  }
248
259
 
249
- /**
250
- * Sets up a consumer for kernel messages
251
- */
252
- public consumeMessages(
253
- consumer: (message: IslandsKernelMessage) => void,
260
+ /** Receive each running session's marimo notifications. */
261
+ public consumeNotifications(
262
+ consumer: (
263
+ notification: NotificationMessage,
264
+ sessionGeneration: number,
265
+ ) => void,
254
266
  ): void {
255
- this.messageConsumer = consumer;
256
- this.rpc.proxy.send.consumerReady({});
267
+ this.notificationConsumer = consumer;
257
268
  }
258
269
 
259
270
  // ============================================================================
@@ -54,7 +54,6 @@ function startSession(controller: TestController, code: string) {
54
54
  return controller.startSession({
55
55
  code,
56
56
  filename: `${code}.py`,
57
- onMessage: vi.fn(),
58
57
  });
59
58
  }
60
59