@marimo-team/frontend 0.25.2-dev66 → 0.25.2-dev89

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 (153) hide show
  1. package/dist/assets/{CellStatus-CRFBVGjI.js → CellStatus-Bj7PvY_u.js} +1 -1
  2. package/dist/assets/{ErrorBoundary-BKmN7zye.js → ErrorBoundary-CG6gtgym.js} +2 -2
  3. package/dist/assets/{JsonOutput-nqqzL-n0.js → JsonOutput-NVH7GWyC.js} +1 -1
  4. package/dist/assets/{MarimoErrorOutput-DCu6fm2y.js → MarimoErrorOutput-z5iFryzf.js} +1 -1
  5. package/dist/assets/{RenderHTML-DRMkieKn.js → RenderHTML-C7QcdAgB.js} +1 -1
  6. package/dist/assets/{RunButton-CPZWjCL9.js → RunButton-A__3wcpF.js} +1 -1
  7. package/dist/assets/{add-cell-with-ai-D20NyZO4.js → add-cell-with-ai-Wyt6GBhk.js} +1 -1
  8. package/dist/assets/{add-connection-dialog-pQcdKIlO.js → add-connection-dialog-bl8dqCVs.js} +1 -1
  9. package/dist/assets/{agent-panel-tNErBgmq.js → agent-panel-BcoO8S6Q.js} +1 -1
  10. package/dist/assets/{ai-model-dropdown-BIeRPKed.js → ai-model-dropdown-CvJsRj9y.js} +1 -1
  11. package/dist/assets/{api-DYL9A86_.js → api-DVLtXyp7.js} +1 -1
  12. package/dist/assets/{app-config-button-DkNGV0b1.js → app-config-button-C7QrZbqr.js} +1 -1
  13. package/dist/assets/{blob-BIYte8oT.js → blob-Bx8A10xl.js} +1 -1
  14. package/dist/assets/{cell-editor-C5RTy4Y0.js → cell-editor-CevZDS63.js} +4 -4
  15. package/dist/assets/cell-link-DC1UZQr_.js +1 -0
  16. package/dist/assets/{cells-h5u00Nz8.js → cells-CxY8RSp7.js} +2 -2
  17. package/dist/assets/{chat-display-DzTkUWMw.js → chat-display-YETh6yXV.js} +1 -1
  18. package/dist/assets/{chat-panel-CKqEpnjN.js → chat-panel-DyaePe7B.js} +1 -1
  19. package/dist/assets/{chat-ui-CGzOsYVb.js → chat-ui-D_QL46qM.js} +1 -1
  20. package/dist/assets/code-visibility-DNzJgrTz.js +1 -0
  21. package/dist/assets/{column-preview-DtrgJzVI.js → column-preview-sL_Jo0hX.js} +1 -1
  22. package/dist/assets/{command-palette-BKYGTfS5.js → command-palette-DMQFuWz1.js} +1 -1
  23. package/dist/assets/{common-BMeq0Bqi.js → common-Cy3lJDvJ.js} +1 -1
  24. package/dist/assets/{components-CWcf8098.js → components-D9ZIJGG8.js} +1 -1
  25. package/dist/assets/{config-BAnYSdpO.js → config-D_c_6ce1.js} +1 -1
  26. package/dist/assets/{datasource-BLHGFSsT.js → datasource-Tst92Pzi.js} +1 -1
  27. package/dist/assets/{dependency-graph-panel-C8X3Nn8W.js → dependency-graph-panel-BwMayTyZ.js} +1 -1
  28. package/dist/assets/{documentation-panel-C8wrSKlX.js → documentation-panel-OMUbXgKW.js} +1 -1
  29. package/dist/assets/{download-CUgiCx7s.js → download-CPMAf7re.js} +1 -1
  30. package/dist/assets/{edit-page-CqPNBpAP.js → edit-page-DD1NFkiu.js} +6 -6
  31. package/dist/assets/{error-entries-DQD4A9cx.js → error-entries-DBpwg0GC.js} +1 -1
  32. package/dist/assets/{error-panel-BJYkwYEY.js → error-panel-ByOEJuZN.js} +1 -1
  33. package/dist/assets/{feedback-button-DbTjGwcX.js → feedback-button-7KY7nL-j.js} +2 -2
  34. package/dist/assets/feedback-button-DxjoRfo3.js +1 -0
  35. package/dist/assets/{file-explorer-panel-BQi88S1o.js → file-explorer-panel-B92ugwxr.js} +1 -1
  36. package/dist/assets/{file-name-input-DPJSxW_X.js → file-name-input-DZehOHLA.js} +1 -1
  37. package/dist/assets/{floating-outline-3PlTr0K1.js → floating-outline-K7cVdOlC.js} +1 -1
  38. package/dist/assets/{gallery-page-BqvVmwde.js → gallery-page-DoriMrLH.js} +1 -1
  39. package/dist/assets/{glide-data-editor-CAa14cIr.js → glide-data-editor-DWyeHe3u.js} +1 -1
  40. package/dist/assets/{home-page-Bg-WqjiD.js → home-page-BcJJsD_Q.js} +1 -1
  41. package/dist/assets/{hooks-DLBvBo8c.js → hooks-DNPsQf79.js} +1 -1
  42. package/dist/assets/index-B7ZZVUcj.css +2 -0
  43. package/dist/assets/{index-BtV91sqJ.js → index-CqJKdR1j.js} +5 -5
  44. package/dist/assets/{layout-mWXZofX2.js → layout-BaluekG6.js} +3 -3
  45. package/dist/assets/{logs-panel-IKL6F2OX.js → logs-panel-OkoO5mbN.js} +1 -1
  46. package/dist/assets/{markdown-renderer-DHvAXF8E.js → markdown-renderer-mDlWBE1M.js} +1 -1
  47. package/dist/assets/{name-cell-input-j4eGLLyd.js → name-cell-input-D_Q2IhP9.js} +1 -1
  48. package/dist/assets/{outline-panel-CprDWmxn.js → outline-panel-Dy2cmYDF.js} +1 -1
  49. package/dist/assets/packages-panel-CIvhx0hb.js +1 -0
  50. package/dist/assets/{panel-accordion-state-8xXVhawu.js → panel-accordion-state-BI4SU22L.js} +1 -1
  51. package/dist/assets/panels-CO6Clgel.js +4 -0
  52. package/dist/assets/{process-output-DOmeJ1VR.js → process-output-Crm0vqbi.js} +1 -1
  53. package/dist/assets/{readonly-code-display-xvH6MyT6.js → readonly-code-display-CNbw6v1-.js} +1 -1
  54. package/dist/assets/{readonly-python-code-D4YGmSvB.js → readonly-python-code-DcYhdd1B.js} +1 -1
  55. package/dist/assets/{reveal-component-CDUG1x74.js → reveal-component-DeZTBbnn.js} +1 -1
  56. package/dist/assets/{run-page-BAqLgcq9.js → run-page-BNEg-Ro9.js} +1 -1
  57. package/dist/assets/runtime-details-state-lxXYwJcI.js +1 -0
  58. package/dist/assets/{scratchpad-panel-BFbUuzdn.js → scratchpad-panel-lDJWcpVs.js} +1 -1
  59. package/dist/assets/{session-panel-BdN43Lzi.js → session-panel-BqutyoYd.js} +1 -1
  60. package/dist/assets/{snippets-panel-5_jR08Zd.js → snippets-panel-YttkYAW9.js} +1 -1
  61. package/dist/assets/state-B2ZbImvm.js +1 -0
  62. package/dist/assets/{state-BxJ_FX3k.js → state-B85OOscw.js} +1 -1
  63. package/dist/assets/{state-7os84dnI.js → state-sbCCk7ZP.js} +1 -1
  64. package/dist/assets/{terminal-DHWTOXu3.js → terminal-3hxM6K9q.js} +1 -1
  65. package/dist/assets/{tracing-CRkNpBaH.js → tracing-DZYfCw6p.js} +1 -1
  66. package/dist/assets/{tracing-panel-DV2-2TUI.js → tracing-panel-BUPJ77jB.js} +2 -2
  67. package/dist/assets/{tree-actions-CShqSonp.js → tree-actions-D5clvgKl.js} +1 -1
  68. package/dist/assets/{useAddCell-DesMfnE4.js → useAddCell-Bhv6GT3m.js} +1 -1
  69. package/dist/assets/{useBoolean-BofUVeKg.js → useBoolean-Co-_MyFz.js} +1 -1
  70. package/dist/assets/{useCellActionButton-BO5SFMxA.js → useCellActionButton-DwAUf_Cn.js} +1 -1
  71. package/dist/assets/{useDeleteCell-DSbUjpSC.js → useDeleteCell-BKN7t4DH.js} +1 -1
  72. package/dist/assets/{useDependencyPanelTab-JIFiQrr2.js → useDependencyPanelTab-CbcGEmqM.js} +1 -1
  73. package/dist/assets/{useInstallPackage-CCQcFc6U.js → useInstallPackage-HTUlGg0U.js} +1 -1
  74. package/dist/assets/{useNotebookActions-CjjED3t-.js → useNotebookActions-pXzVjrlk.js} +1 -1
  75. package/dist/assets/{useRunCells-Bx3wXUWO.js → useRunCells-zMO43eNR.js} +1 -1
  76. package/dist/assets/{useSplitCell-BRmQP02b.js → useSplitCell-xEtFLByO.js} +1 -1
  77. package/dist/assets/{vega-component-C28fL5fD.js → vega-component-Chbfi__Q.js} +1 -1
  78. package/dist/index.html +24 -24
  79. package/package.json +1 -1
  80. package/src/__tests__/mount.test.ts +6 -6
  81. package/src/components/editor/actions/useRestartKernel.tsx +2 -2
  82. package/src/components/editor/alerts/__tests__/connection-notice.test.tsx +224 -17
  83. package/src/components/editor/alerts/connection-details.tsx +61 -0
  84. package/src/components/editor/alerts/connection-notice.tsx +46 -53
  85. package/src/components/editor/alerts/startup-progress.tsx +73 -129
  86. package/src/components/editor/app-container.tsx +2 -2
  87. package/src/components/editor/boundary/__tests__/ErrorBoundary.test.tsx +2 -2
  88. package/src/components/editor/cell/CreateCellButton.tsx +2 -2
  89. package/src/components/editor/cell/DeleteButton.tsx +2 -2
  90. package/src/components/editor/cell/RunButton.tsx +3 -3
  91. package/src/components/editor/cell/StopButton.tsx +2 -2
  92. package/src/components/editor/chrome/components/__tests__/feedback-button.test.tsx +4 -4
  93. package/src/components/editor/chrome/components/feedback-button.tsx +3 -3
  94. package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +8 -8
  95. package/src/components/editor/chrome/panels/__tests__/{sandbox-details.test.tsx → runtime-details.test.tsx} +94 -22
  96. package/src/components/editor/chrome/panels/__tests__/sandbox-recovery.test.tsx +4 -4
  97. package/src/components/editor/chrome/panels/packages-panel.tsx +8 -29
  98. package/src/components/editor/chrome/panels/{sandbox-details-state.ts → runtime-details-state.ts} +1 -1
  99. package/src/components/editor/chrome/panels/{sandbox-panel.tsx → runtime-panel.tsx} +38 -66
  100. package/src/components/editor/chrome/panels/runtime-status-toggle.tsx +56 -0
  101. package/src/components/editor/chrome/panels/sandbox-controller.tsx +2 -2
  102. package/src/components/editor/chrome/wrapper/app-chrome.tsx +3 -3
  103. package/src/components/editor/chrome/wrapper/footer-items/machine-stats.tsx +2 -2
  104. package/src/components/editor/controls/Controls.tsx +3 -3
  105. package/src/components/editor/controls/__tests__/notebook-menu-dropdown.test.tsx +2 -2
  106. package/src/components/editor/header/__tests__/status.test.tsx +4 -4
  107. package/src/components/editor/header/app-header.tsx +2 -2
  108. package/src/components/editor/header/status.tsx +3 -3
  109. package/src/components/ui/progress-stages.tsx +114 -0
  110. package/src/core/codemirror/lsp/__tests__/notebook-lsp.test.ts +106 -5
  111. package/src/core/codemirror/lsp/notebook-lsp.ts +16 -2
  112. package/src/core/codemirror/readonly/__tests__/extension.test.ts +6 -6
  113. package/src/core/codemirror/readonly/extension.ts +2 -2
  114. package/src/core/datasets/__tests__/data-source-discovery.test.ts +2 -2
  115. package/src/core/edit-app.tsx +5 -1
  116. package/src/core/export/hooks.ts +2 -2
  117. package/src/core/network/__tests__/connection.test.ts +3 -3
  118. package/src/core/network/connection-notice.ts +5 -5
  119. package/src/core/network/connection.ts +9 -9
  120. package/src/core/network/requests-lazy.ts +2 -2
  121. package/src/core/packages/__tests__/useSandboxController.test.tsx +2 -2
  122. package/src/core/packages/useSandboxController.ts +6 -6
  123. package/src/core/runtime/__tests__/adapter.test.ts +6 -6
  124. package/src/core/runtime/adapter.ts +7 -7
  125. package/src/core/runtime/config.ts +2 -2
  126. package/src/core/saving/filename.ts +2 -2
  127. package/src/core/saving/save-component.tsx +5 -5
  128. package/src/core/saving/useAutoSave.ts +2 -2
  129. package/src/core/websocket/__tests__/useMarimoKernelConnection.hook.test.tsx +30 -25
  130. package/src/core/websocket/__tests__/useMarimoKernelConnection.test.ts +5 -5
  131. package/src/core/websocket/connection-utils.ts +21 -21
  132. package/src/core/websocket/types.ts +8 -9
  133. package/src/core/websocket/useMarimoKernelConnection.tsx +21 -21
  134. package/src/mount.tsx +2 -2
  135. package/src/plugins/core/__test__/registerReactComponent.test.ts +55 -0
  136. package/src/plugins/core/registerReactComponent.tsx +3 -1
  137. package/src/stories/cell.stories.tsx +2 -2
  138. package/src/stories/layout/vertical/one-column.stories.tsx +5 -5
  139. package/src/stories/progress-stages.stories.tsx +62 -0
  140. package/src/stories/runtime-existing.stories.tsx +44 -0
  141. package/src/stories/runtime-maintenance.stories.tsx +40 -0
  142. package/src/stories/runtime-pixi.stories.tsx +28 -0
  143. package/src/stories/{sandbox.stories.tsx → runtime-story.tsx} +191 -152
  144. package/src/stories/runtime-uv.stories.tsx +56 -0
  145. package/dist/assets/cell-link-BeZAiE1D.js +0 -1
  146. package/dist/assets/code-visibility-B5QjA0ie.js +0 -1
  147. package/dist/assets/feedback-button-JqvnTSfN.js +0 -1
  148. package/dist/assets/index-CF8Gfz1R.css +0 -2
  149. package/dist/assets/packages-panel-CDDChziK.js +0 -1
  150. package/dist/assets/panels-CU7deLqc.js +0 -4
  151. package/dist/assets/sandbox-panel-D5bRunTM.js +0 -1
  152. package/dist/assets/state-Bo8h64Rn.js +0 -1
  153. package/src/components/editor/chrome/panels/sandbox-toggle.tsx +0 -52
@@ -7,7 +7,7 @@ import { memoizeLastValue } from "@/utils/once";
7
7
  import { waitForKernelToBeInstantiated } from "../kernel/state";
8
8
  import type { RuntimeManager } from "../runtime/runtime";
9
9
  import { store } from "../state/jotai";
10
- import { WebSocketState } from "../websocket/types";
10
+ import { ConnectionState } from "../websocket/types";
11
11
  import { connectionAtom, waitForConnectionOpen } from "./connection";
12
12
  import type { EditRequests, RunRequests } from "./types";
13
13
 
@@ -159,7 +159,7 @@ export function createLazyRequests(
159
159
  ): AllRequests {
160
160
  // Memoize the init call, just once per runtime manager
161
161
  const initOnce = memoizeLastValue(async (runtimeManager: RuntimeManager) => {
162
- store.set(connectionAtom, { state: WebSocketState.CONNECTING });
162
+ store.set(connectionAtom, { state: ConnectionState.CONNECTING });
163
163
  await runtimeManager.init();
164
164
  });
165
165
 
@@ -17,7 +17,7 @@ import type {
17
17
  } from "@/core/network/types";
18
18
  import { filenameAtom } from "@/core/saving/file-state";
19
19
  import { store } from "@/core/state/jotai";
20
- import { WebSocketState } from "@/core/websocket/types";
20
+ import { ConnectionState } from "@/core/websocket/types";
21
21
  import { HTTPError } from "@/utils/errors";
22
22
  import { packageDataVersionAtom } from "../package-data";
23
23
  import {
@@ -52,7 +52,7 @@ beforeEach(() => {
52
52
  store.set(sandboxAtom, null);
53
53
  store.set(sandboxSyncAtom, { kind: "succeeded" });
54
54
  store.set(sandboxActionsAtom, null);
55
- store.set(connectionAtom, { state: WebSocketState.OPEN });
55
+ store.set(connectionAtom, { state: ConnectionState.OPEN });
56
56
  store.set(kernelStartupErrorAtom, null);
57
57
  store.set(filenameAtom, "notebook.py");
58
58
  store.set(packageDataVersionAtom, 0);
@@ -8,7 +8,7 @@ import { useRequestClient } from "@/core/network/requests";
8
8
  import { useFilename } from "@/core/saving/filename";
9
9
  import { waitFor } from "@/core/state/jotai";
10
10
  import { isWasm } from "@/core/wasm/utils";
11
- import { WebSocketState } from "@/core/websocket/types";
11
+ import { ConnectionState } from "@/core/websocket/types";
12
12
  import { HTTPError, prettyError } from "@/utils/errors";
13
13
  import { invalidatePackageData } from "./package-data";
14
14
  import {
@@ -67,7 +67,7 @@ export function useSandboxController(onReconnect: () => Promise<void>) {
67
67
  if (
68
68
  inFlight.current ||
69
69
  operation.kind === "running" ||
70
- connection.state === WebSocketState.CONNECTING
70
+ connection.state === ConnectionState.CONNECTING
71
71
  ) {
72
72
  return false;
73
73
  }
@@ -91,10 +91,10 @@ export function useSandboxController(onReconnect: () => Promise<void>) {
91
91
  const status = await waitFor(
92
92
  connectionAtom,
93
93
  (value) =>
94
- value.state === WebSocketState.OPEN ||
95
- value.state === WebSocketState.CLOSED,
94
+ value.state === ConnectionState.OPEN ||
95
+ value.state === ConnectionState.CLOSED,
96
96
  );
97
- if (status.state === WebSocketState.CLOSED) {
97
+ if (status.state === ConnectionState.CLOSED) {
98
98
  setOperation({ kind: "idle" });
99
99
  return false;
100
100
  }
@@ -177,7 +177,7 @@ export function useSandboxController(onReconnect: () => Promise<void>) {
177
177
  saving ||
178
178
  loading ||
179
179
  operation.kind === "running" ||
180
- connection.state === WebSocketState.CONNECTING;
180
+ connection.state === ConnectionState.CONNECTING;
181
181
  const diagnostic =
182
182
  error ??
183
183
  (operation.kind === "failed"
@@ -6,7 +6,7 @@ import { islandsInitializedAtom } from "../../islands/state";
6
6
  import { kernelStateAtom } from "../../kernel/state";
7
7
  import { connectionAtom } from "../../network/connection";
8
8
  import { wasmInitStateAtom } from "../../wasm/state";
9
- import { WebSocketState } from "../../websocket/types";
9
+ import { ConnectionState } from "../../websocket/types";
10
10
  import {
11
11
  canRunCellsAtom,
12
12
  islandsAdapter,
@@ -50,13 +50,13 @@ describe("wasmAdapter", () => {
50
50
 
51
51
  describe("remoteAdapter", () => {
52
52
  it.each([
53
- [{ state: WebSocketState.OPEN }, { kind: "ready" }],
53
+ [{ state: ConnectionState.OPEN }, { kind: "ready" }],
54
54
  [
55
- { state: WebSocketState.CONNECTING },
55
+ { state: ConnectionState.CONNECTING },
56
56
  { kind: "connecting", progress: { label: "Connecting…" } },
57
57
  ],
58
58
  [
59
- { state: WebSocketState.NOT_STARTED },
59
+ { state: ConnectionState.NOT_STARTED },
60
60
  { kind: "connecting", progress: { label: "Not connected" } },
61
61
  ],
62
62
  ] as const)("maps %j to %j", (conn, expected) => {
@@ -68,7 +68,7 @@ describe("remoteAdapter", () => {
68
68
  it("surfaces the close reason when closed", () => {
69
69
  const store = createStore();
70
70
  store.set(connectionAtom, {
71
- state: WebSocketState.CLOSED,
71
+ state: ConnectionState.CLOSED,
72
72
  code: "KERNEL_DISCONNECTED",
73
73
  reason: "Kernel went away",
74
74
  });
@@ -125,7 +125,7 @@ describe("canRunCellsAtom", () => {
125
125
  it("is false on a remote adapter until kernel acks", () => {
126
126
  const store = createStore();
127
127
  // Default adapter in test env is remote.
128
- store.set(connectionAtom, { state: WebSocketState.OPEN });
128
+ store.set(connectionAtom, { state: ConnectionState.OPEN });
129
129
  expect(store.get(canRunCellsAtom)).toBe(false);
130
130
 
131
131
  store.set(kernelStateAtom, { isInstantiated: true, error: null });
@@ -8,7 +8,7 @@ import { connectionAtom } from "../network/connection";
8
8
  import { isStaticNotebook } from "../static/static-state";
9
9
  import { wasmInitStateAtom } from "../wasm/state";
10
10
  import { isWasm } from "../wasm/utils";
11
- import { WebSocketState } from "../websocket/types";
11
+ import { ConnectionState } from "../websocket/types";
12
12
 
13
13
  export type RuntimeKind = "wasm" | "remote" | "static" | "islands";
14
14
 
@@ -76,19 +76,19 @@ export const wasmAdapter: RuntimeAdapter = {
76
76
  const remoteStateAtom = atom<AdapterState>((get) => {
77
77
  const conn = get(connectionAtom);
78
78
  switch (conn.state) {
79
- case WebSocketState.OPEN:
79
+ case ConnectionState.OPEN:
80
80
  return { kind: "ready" };
81
- case WebSocketState.CONNECTING:
81
+ case ConnectionState.CONNECTING:
82
82
  return { kind: "connecting", progress: { label: "Connecting…" } };
83
- case WebSocketState.NOT_STARTED:
83
+ case ConnectionState.NOT_STARTED:
84
84
  return { kind: "connecting", progress: { label: "Not connected" } };
85
- case WebSocketState.CLOSING:
86
- case WebSocketState.CLOSED:
85
+ case ConnectionState.CLOSING:
86
+ case ConnectionState.CLOSED:
87
87
  return {
88
88
  kind: "failed",
89
89
  error: {
90
90
  message:
91
- conn.state === WebSocketState.CLOSED
91
+ conn.state === ConnectionState.CLOSED
92
92
  ? conn.reason
93
93
  : "Disconnecting",
94
94
  errorKind: "runtime",
@@ -5,7 +5,7 @@ import { Logger } from "@/utils/Logger";
5
5
  import { connectionAtom } from "../network/connection";
6
6
  import { store } from "../state/jotai";
7
7
  import { isAppNotStarted } from "../websocket/connection-utils";
8
- import { WebSocketState } from "../websocket/types";
8
+ import { ConnectionState } from "../websocket/types";
9
9
  import { RuntimeManager } from "./runtime";
10
10
  import type { RuntimeConfig } from "./types";
11
11
 
@@ -36,7 +36,7 @@ export function useConnectToRuntime(): () => Promise<void> {
36
36
  const [connection, setConnection] = useAtom(connectionAtom);
37
37
  return useEvent(async () => {
38
38
  if (isAppNotStarted(connection.state)) {
39
- setConnection({ state: WebSocketState.CONNECTING });
39
+ setConnection({ state: ConnectionState.CONNECTING });
40
40
  await runtimeManager.init();
41
41
  } else {
42
42
  Logger.log("Runtime already started or starting...");
@@ -9,7 +9,7 @@ import { getAppConfig } from "../config/config";
9
9
  import { KnownQueryParams } from "../constants";
10
10
  import { connectionAtom } from "../network/connection";
11
11
  import { useRequestClient } from "../network/requests";
12
- import { WebSocketState } from "../websocket/types";
12
+ import { ConnectionState } from "../websocket/types";
13
13
  import { filenameAtom } from "./file-state";
14
14
 
15
15
  export function useFilename() {
@@ -24,7 +24,7 @@ export function useUpdateFilename() {
24
24
 
25
25
  const handleFilenameChange = useEvent(async (name: string) => {
26
26
  const appConfig = getAppConfig();
27
- if (connection.state !== WebSocketState.OPEN) {
27
+ if (connection.state !== ConnectionState.OPEN) {
28
28
  openAlert("Failed to save notebook: not connected to a kernel.");
29
29
  return null;
30
30
  }
@@ -31,7 +31,7 @@ import { layoutStateAtom } from "../layout/state";
31
31
  import { kioskModeAtom } from "../mode";
32
32
  import { connectionAtom } from "../network/connection";
33
33
  import { useRequestClient } from "../network/requests";
34
- import { WebSocketState } from "../websocket/types";
34
+ import { ConnectionState } from "../websocket/types";
35
35
  import { filenameAtom } from "./file-state";
36
36
  import { useFilename, useUpdateFilename } from "./filename";
37
37
  import { lastSavedNotebookAtom, needsSaveAtom } from "./state";
@@ -55,7 +55,7 @@ export function enqueueNotebookSave<T>(save: () => Promise<T>): Promise<T> {
55
55
  export const SaveComponent = ({ kioskMode }: SaveNotebookProps) => {
56
56
  const filename = useFilename();
57
57
  const needsSave = useAtomValue(needsSaveAtom);
58
- const closed = useAtomValue(connectionAtom).state === WebSocketState.CLOSED;
58
+ const closed = useAtomValue(connectionAtom).state === ConnectionState.CLOSED;
59
59
  const { saveOrNameNotebook, saveIfNotebookIsPersistent } = useSaveNotebook();
60
60
  useAutoSaveNotebook({ onSave: saveIfNotebookIsPersistent, kioskMode });
61
61
 
@@ -120,7 +120,7 @@ export function useSaveNotebook() {
120
120
  }
121
121
 
122
122
  // Don't save if we are not connected to a kernel
123
- if (connection.state !== WebSocketState.OPEN) {
123
+ if (connection.state !== ConnectionState.OPEN) {
124
124
  openAlert("Failed to save notebook: not connected to a kernel.");
125
125
  return;
126
126
  }
@@ -168,7 +168,7 @@ export function useSaveNotebook() {
168
168
  const connection = store.get(connectionAtom);
169
169
  if (
170
170
  isNamedPersistentFile(filename) &&
171
- connection.state === WebSocketState.OPEN
171
+ connection.state === ConnectionState.OPEN
172
172
  ) {
173
173
  saveNotebook(filename, userInitiated);
174
174
  }
@@ -191,7 +191,7 @@ export function useSaveNotebook() {
191
191
  // Filename does not exist and we are connected to a kernel
192
192
  if (
193
193
  !isNamedPersistentFile(filename) &&
194
- connection.state !== WebSocketState.CLOSED
194
+ connection.state !== ConnectionState.CLOSED
195
195
  ) {
196
196
  openModal(<SaveDialog onClose={closeModal} onSave={handleSaveDialog} />);
197
197
  }
@@ -2,7 +2,7 @@
2
2
  import { useEffect, useRef } from "react";
3
3
  import type { UserConfig } from "../config/config-schema";
4
4
  import type { CellConfig } from "../network/types";
5
- import { type ConnectionStatus, WebSocketState } from "../websocket/types";
5
+ import { type ConnectionStatus, ConnectionState } from "../websocket/types";
6
6
 
7
7
  export function useAutoSave(opts: {
8
8
  codes: string[];
@@ -43,7 +43,7 @@ export function useAutoSave(opts: {
43
43
  clearTimeout(autosaveTimeoutId.current);
44
44
  }
45
45
 
46
- if (needsSave && connStatus.state === WebSocketState.OPEN) {
46
+ if (needsSave && connStatus.state === ConnectionState.OPEN) {
47
47
  autosaveTimeoutId.current = setTimeout(onSave, config.autosave_delay);
48
48
  }
49
49
  }
@@ -52,7 +52,7 @@ import { useRuntimeManager } from "@/core/runtime/config";
52
52
  import { initialRunCompletedAtom } from "../../kernel/state";
53
53
  import { connectionAtom, startupProgressAtom } from "../../network/connection";
54
54
  import type { SessionId } from "../../kernel/session";
55
- import { WebSocketClosedReason, WebSocketState } from "../types";
55
+ import { WebSocketClosedReason, ConnectionState } from "../types";
56
56
  import type { IConnectionTransport } from "../transports/transport";
57
57
  import { useMarimoKernelConnection } from "../useMarimoKernelConnection";
58
58
  import { useConnectionTransport } from "../useWebSocket";
@@ -118,7 +118,7 @@ describe("useMarimoKernelConnection.reconnect()", () => {
118
118
  isHealthy = vi.fn<() => Promise<boolean>>().mockResolvedValue(true);
119
119
  store = createStore();
120
120
  store.set(connectionAtom, {
121
- state: WebSocketState.CLOSED,
121
+ state: ConnectionState.CLOSED,
122
122
  code: WebSocketClosedReason.KERNEL_DISCONNECTED,
123
123
  reason: "kernel not found",
124
124
  });
@@ -163,7 +163,7 @@ describe("useMarimoKernelConnection.reconnect()", () => {
163
163
  expect(isHealthy).toHaveBeenCalledOnce();
164
164
  expect(transport.reconnect).toHaveBeenCalledOnce();
165
165
  expect(store.get(connectionAtom)).toEqual({
166
- state: WebSocketState.CONNECTING,
166
+ state: ConnectionState.CONNECTING,
167
167
  });
168
168
  });
169
169
 
@@ -176,7 +176,7 @@ describe("useMarimoKernelConnection.reconnect()", () => {
176
176
  expect(isHealthy).toHaveBeenCalledOnce();
177
177
  expect(transport.reconnect).not.toHaveBeenCalled();
178
178
  expect(store.get(connectionAtom)).toEqual({
179
- state: WebSocketState.CLOSED,
179
+ state: ConnectionState.CLOSED,
180
180
  code: WebSocketClosedReason.KERNEL_DISCONNECTED,
181
181
  reason: "kernel not found",
182
182
  });
@@ -215,7 +215,7 @@ it.each(["kernel-ready", "reconnected"])(
215
215
  "waits for %s before marking the session connected",
216
216
  async (op) => {
217
217
  const store = createStore();
218
- store.set(connectionAtom, { state: WebSocketState.CONNECTING });
218
+ store.set(connectionAtom, { state: ConnectionState.CONNECTING });
219
219
  vi.mocked(useConnectionTransport).mockClear();
220
220
  vi.mocked(useConnectionTransport).mockReturnValue(
221
221
  makeTransport(WebSocket.OPEN),
@@ -229,7 +229,7 @@ it.each(["kernel-ready", "reconnected"])(
229
229
  await act(async () => {
230
230
  await options.onOpen(new Event("open"));
231
231
  });
232
- expect(store.get(connectionAtom).state).toBe(WebSocketState.CONNECTING);
232
+ expect(store.get(connectionAtom).state).toBe(ConnectionState.CONNECTING);
233
233
 
234
234
  act(() => {
235
235
  options.onMessage(
@@ -257,7 +257,7 @@ it.each(["kernel-ready", "reconnected"])(
257
257
  }),
258
258
  );
259
259
  });
260
- expect(store.get(connectionAtom).state).toBe(WebSocketState.OPEN);
260
+ expect(store.get(connectionAtom).state).toBe(ConnectionState.OPEN);
261
261
  },
262
262
  );
263
263
 
@@ -267,7 +267,7 @@ describe("connection notice", () => {
267
267
 
268
268
  function renderNotice() {
269
269
  const store = createStore();
270
- store.set(connectionAtom, { state: WebSocketState.CONNECTING });
270
+ store.set(connectionAtom, { state: ConnectionState.CONNECTING });
271
271
  const transport = makeTransport(WebSocket.OPEN);
272
272
  vi.mocked(useConnectionTransport).mockReturnValue(transport);
273
273
  vi.mocked(useRuntimeManager).mockReturnValue(
@@ -322,14 +322,6 @@ describe("connection notice", () => {
322
322
  );
323
323
  act(() => vi.advanceTimersByTime(35_000));
324
324
  expect(screen.getByText("Elapsed 35s")).toBeInTheDocument();
325
- act(() =>
326
- store.set(
327
- notebookAtom,
328
- MockNotebook.notebookState({
329
- cellData: { [cellId("test")]: { code: "answer = 42" } },
330
- }),
331
- ),
332
- );
333
325
  send({
334
326
  op: "startup-progress",
335
327
  phase: "starting-kernel",
@@ -338,6 +330,18 @@ describe("connection notice", () => {
338
330
  });
339
331
  expect(screen.getByRole("status")).toHaveTextContent("Starting kernel");
340
332
  expect(screen.getByText("Elapsed 35s")).toBeInTheDocument();
333
+ act(() =>
334
+ store.set(
335
+ notebookAtom,
336
+ MockNotebook.notebookState({
337
+ cellData: { [cellId("test")]: { code: "answer = 42" } },
338
+ }),
339
+ ),
340
+ );
341
+ expect(
342
+ screen.getByRole("button", { name: "Starting kernel… Open Packages" }),
343
+ ).toBeVisible();
344
+ expect(screen.queryByText(/Elapsed/)).not.toBeInTheDocument();
341
345
  send({ op: "reconnected" });
342
346
  expect(
343
347
  screen.queryByRole("region", { name: "Notebook startup" }),
@@ -345,7 +349,7 @@ describe("connection notice", () => {
345
349
  act(() => options.onClose(new CloseEvent("close")));
346
350
  act(() => vi.advanceTimersByTime(500));
347
351
  expect(store.get(connectionAtom)).toEqual({
348
- state: WebSocketState.CONNECTING,
352
+ state: ConnectionState.CONNECTING,
349
353
  phase: "reconnecting",
350
354
  });
351
355
  expect(
@@ -388,7 +392,7 @@ describe("connection notice", () => {
388
392
  logs: "Kernel startup output\n",
389
393
  log_mode: "replace",
390
394
  });
391
- expect(store.get(connectionAtom)).toEqual({ state: WebSocketState.OPEN });
395
+ expect(store.get(connectionAtom)).toEqual({ state: ConnectionState.OPEN });
392
396
  expect(store.get(startupProgressAtom)).toEqual({
393
397
  phase: "starting-kernel",
394
398
  logs: "Kernel startup output\n",
@@ -429,12 +433,9 @@ describe("connection notice", () => {
429
433
  ).not.toBeInTheDocument();
430
434
  });
431
435
 
432
- it.each([
433
- ["preparing-environment", "Sandbox setup failed"],
434
- ["starting-kernel", "Kernel failed to start"],
435
- ] as const)(
436
+ it.each(["preparing-environment", "starting-kernel"] as const)(
436
437
  "keeps a %s failure available until the user retries",
437
- async (phase, title) => {
438
+ async (phase) => {
438
439
  const { store, transport, options, send } = renderNotice();
439
440
  send({ op: "startup-progress", phase, logs: "", log_mode: "replace" });
440
441
  const error = "A full diagnostic\nwith <stderr> details";
@@ -445,9 +446,13 @@ describe("connection notice", () => {
445
446
  new CloseEvent("close", { reason: "MARIMO_KERNEL_STARTUP_ERROR" }),
446
447
  ),
447
448
  );
448
- expect(screen.getByRole("status")).toHaveTextContent(title);
449
+ expect(screen.getByRole("status")).toHaveTextContent(
450
+ "Kernel failed to start",
451
+ );
449
452
  act(() => vi.advanceTimersByTime(30_000));
450
- expect(screen.getByRole("status")).toHaveTextContent(title);
453
+ expect(screen.getByRole("status")).toHaveTextContent(
454
+ "Kernel failed to start",
455
+ );
451
456
  expect(screen.getByLabelText("Error details")).toHaveTextContent(error, {
452
457
  normalizeWhitespace: false,
453
458
  });
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { afterEach, describe, expect, it, vi } from "vitest";
4
4
  import { Logger } from "@/utils/Logger";
5
- import { WebSocketClosedReason, WebSocketState } from "../types";
5
+ import { WebSocketClosedReason, ConnectionState } from "../types";
6
6
  import { classifyCloseEvent } from "../useMarimoKernelConnection";
7
7
 
8
8
  function classify(reason: string | undefined) {
@@ -14,7 +14,7 @@ describe("classifyCloseEvent", () => {
14
14
  it("retries on empty/undefined reason", () => {
15
15
  const decision = classify(undefined);
16
16
  expect(decision.kind).toBe("retry");
17
- expect(decision.status).toEqual({ state: WebSocketState.CONNECTING });
17
+ expect(decision.status).toEqual({ state: ConnectionState.CONNECTING });
18
18
  });
19
19
 
20
20
  it("treats unknown reason strings as transient and logs a warning", () => {
@@ -44,7 +44,7 @@ describe("classifyCloseEvent", () => {
44
44
  const decision = classify(reason);
45
45
  expect(decision.kind).toBe("terminal");
46
46
  expect(decision.status).toMatchObject({
47
- state: WebSocketState.CLOSED,
47
+ state: ConnectionState.CLOSED,
48
48
  code: WebSocketClosedReason.KERNEL_DISCONNECTED,
49
49
  });
50
50
  expect(decision).toMatchObject({
@@ -57,7 +57,7 @@ describe("classifyCloseEvent", () => {
57
57
  const decision = classify("MARIMO_KERNEL_STARTUP_ERROR");
58
58
  expect(decision.kind).toBe("terminal");
59
59
  expect(decision.status).toMatchObject({
60
- state: WebSocketState.CLOSED,
60
+ state: ConnectionState.CLOSED,
61
61
  code: WebSocketClosedReason.KERNEL_STARTUP_ERROR,
62
62
  });
63
63
  expect(decision).toMatchObject({
@@ -72,7 +72,7 @@ describe("classifyCloseEvent", () => {
72
72
  const decision = classify("MARIMO_TRANSPORT_EXHAUSTED");
73
73
  expect(decision.kind).toBe("gave-up");
74
74
  expect(decision.status).toEqual({
75
- state: WebSocketState.CLOSED,
75
+ state: ConnectionState.CLOSED,
76
76
  code: WebSocketClosedReason.KERNEL_DISCONNECTED,
77
77
  reason: "kernel not found",
78
78
  });
@@ -1,66 +1,66 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
- import { WebSocketState } from "./types";
2
+ import { ConnectionState } from "./types";
3
3
 
4
4
  /**
5
5
  * Check if the app is in a closed/disconnected state
6
6
  */
7
- export function isAppClosed(state: WebSocketState): boolean {
8
- return state === WebSocketState.CLOSED;
7
+ export function isAppClosed(state: ConnectionState): boolean {
8
+ return state === ConnectionState.CLOSED;
9
9
  }
10
10
 
11
11
  /**
12
12
  * Check if the app is in a connecting state
13
13
  */
14
- export function isAppConnecting(state: WebSocketState): boolean {
15
- return state === WebSocketState.CONNECTING;
14
+ export function isAppConnecting(state: ConnectionState): boolean {
15
+ return state === ConnectionState.CONNECTING;
16
16
  }
17
17
 
18
18
  /**
19
19
  * Check if the app is in an open/connected state
20
20
  */
21
- export function isAppConnected(state: WebSocketState): boolean {
22
- return state === WebSocketState.OPEN;
21
+ export function isAppConnected(state: ConnectionState): boolean {
22
+ return state === ConnectionState.OPEN;
23
23
  }
24
24
 
25
25
  /**
26
26
  * Check if the app is in a closing state
27
27
  */
28
- export function isAppClosing(state: WebSocketState): boolean {
29
- return state === WebSocketState.CLOSING;
28
+ export function isAppClosing(state: ConnectionState): boolean {
29
+ return state === ConnectionState.CLOSING;
30
30
  }
31
31
 
32
32
  /**
33
33
  * Check if the app is in a not started state
34
34
  */
35
- export function isAppNotStarted(state: WebSocketState): boolean {
36
- return state === WebSocketState.NOT_STARTED;
35
+ export function isAppNotStarted(state: ConnectionState): boolean {
36
+ return state === ConnectionState.NOT_STARTED;
37
37
  }
38
38
 
39
39
  /**
40
40
  * Check if the app is in a state where user interactions should be disabled
41
41
  */
42
- export function isAppInteractionDisabled(state: WebSocketState): boolean {
42
+ export function isAppInteractionDisabled(state: ConnectionState): boolean {
43
43
  return (
44
- state === WebSocketState.CLOSED ||
45
- state === WebSocketState.CLOSING ||
46
- state === WebSocketState.CONNECTING
44
+ state === ConnectionState.CLOSED ||
45
+ state === ConnectionState.CLOSING ||
46
+ state === ConnectionState.CONNECTING
47
47
  );
48
48
  }
49
49
 
50
50
  /**
51
51
  * Get a human-readable tooltip message for the connection state
52
52
  */
53
- export function getConnectionTooltip(state: WebSocketState): string {
53
+ export function getConnectionTooltip(state: ConnectionState): string {
54
54
  switch (state) {
55
- case WebSocketState.CLOSED:
55
+ case ConnectionState.CLOSED:
56
56
  return "App disconnected";
57
- case WebSocketState.CONNECTING:
57
+ case ConnectionState.CONNECTING:
58
58
  return "Connecting to a runtime ...";
59
- case WebSocketState.CLOSING:
59
+ case ConnectionState.CLOSING:
60
60
  return "App disconnecting...";
61
- case WebSocketState.OPEN:
61
+ case ConnectionState.OPEN:
62
62
  return "";
63
- case WebSocketState.NOT_STARTED:
63
+ case ConnectionState.NOT_STARTED:
64
64
  return "Click to connect to a runtime";
65
65
  default:
66
66
  return "";
@@ -6,8 +6,7 @@ export type ConnectionPhase =
6
6
  | NotificationMessageData<"startup-progress">["phase"]
7
7
  | "reconnecting";
8
8
 
9
- // TODO: rename to ConnectionState
10
- export const WebSocketState = {
9
+ export const ConnectionState = {
11
10
  NOT_STARTED: "NOT_STARTED",
12
11
  CONNECTING: "CONNECTING",
13
12
  OPEN: "OPEN",
@@ -15,8 +14,8 @@ export const WebSocketState = {
15
14
  CLOSED: "CLOSED",
16
15
  } as const;
17
16
 
18
- export type WebSocketState =
19
- (typeof WebSocketState)[keyof typeof WebSocketState];
17
+ export type ConnectionState =
18
+ (typeof ConnectionState)[keyof typeof ConnectionState];
20
19
 
21
20
  export const WebSocketClosedReason = {
22
21
  KERNEL_DISCONNECTED: "KERNEL_DISCONNECTED",
@@ -28,7 +27,7 @@ export type WebSocketClosedReason =
28
27
 
29
28
  export type ConnectionStatus =
30
29
  | {
31
- state: typeof WebSocketState.CLOSED;
30
+ state: typeof ConnectionState.CLOSED;
32
31
  code: WebSocketClosedReason;
33
32
  /**
34
33
  * Human-readable reason for closing the connection.
@@ -37,12 +36,12 @@ export type ConnectionStatus =
37
36
  phase?: ConnectionPhase;
38
37
  }
39
38
  | {
40
- state: typeof WebSocketState.CONNECTING;
39
+ state: typeof ConnectionState.CONNECTING;
41
40
  phase?: ConnectionPhase;
42
41
  }
43
42
  | {
44
43
  state:
45
- | typeof WebSocketState.OPEN
46
- | typeof WebSocketState.CLOSING
47
- | typeof WebSocketState.NOT_STARTED;
44
+ | typeof ConnectionState.OPEN
45
+ | typeof ConnectionState.CLOSING
46
+ | typeof ConnectionState.NOT_STARTED;
48
47
  };