@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.
- package/dist/assets/{CellStatus-CRFBVGjI.js → CellStatus-Bj7PvY_u.js} +1 -1
- package/dist/assets/{ErrorBoundary-BKmN7zye.js → ErrorBoundary-CG6gtgym.js} +2 -2
- package/dist/assets/{JsonOutput-nqqzL-n0.js → JsonOutput-NVH7GWyC.js} +1 -1
- package/dist/assets/{MarimoErrorOutput-DCu6fm2y.js → MarimoErrorOutput-z5iFryzf.js} +1 -1
- package/dist/assets/{RenderHTML-DRMkieKn.js → RenderHTML-C7QcdAgB.js} +1 -1
- package/dist/assets/{RunButton-CPZWjCL9.js → RunButton-A__3wcpF.js} +1 -1
- package/dist/assets/{add-cell-with-ai-D20NyZO4.js → add-cell-with-ai-Wyt6GBhk.js} +1 -1
- package/dist/assets/{add-connection-dialog-pQcdKIlO.js → add-connection-dialog-bl8dqCVs.js} +1 -1
- package/dist/assets/{agent-panel-tNErBgmq.js → agent-panel-BcoO8S6Q.js} +1 -1
- package/dist/assets/{ai-model-dropdown-BIeRPKed.js → ai-model-dropdown-CvJsRj9y.js} +1 -1
- package/dist/assets/{api-DYL9A86_.js → api-DVLtXyp7.js} +1 -1
- package/dist/assets/{app-config-button-DkNGV0b1.js → app-config-button-C7QrZbqr.js} +1 -1
- package/dist/assets/{blob-BIYte8oT.js → blob-Bx8A10xl.js} +1 -1
- package/dist/assets/{cell-editor-C5RTy4Y0.js → cell-editor-CevZDS63.js} +4 -4
- package/dist/assets/cell-link-DC1UZQr_.js +1 -0
- package/dist/assets/{cells-h5u00Nz8.js → cells-CxY8RSp7.js} +2 -2
- package/dist/assets/{chat-display-DzTkUWMw.js → chat-display-YETh6yXV.js} +1 -1
- package/dist/assets/{chat-panel-CKqEpnjN.js → chat-panel-DyaePe7B.js} +1 -1
- package/dist/assets/{chat-ui-CGzOsYVb.js → chat-ui-D_QL46qM.js} +1 -1
- package/dist/assets/code-visibility-DNzJgrTz.js +1 -0
- package/dist/assets/{column-preview-DtrgJzVI.js → column-preview-sL_Jo0hX.js} +1 -1
- package/dist/assets/{command-palette-BKYGTfS5.js → command-palette-DMQFuWz1.js} +1 -1
- package/dist/assets/{common-BMeq0Bqi.js → common-Cy3lJDvJ.js} +1 -1
- package/dist/assets/{components-CWcf8098.js → components-D9ZIJGG8.js} +1 -1
- package/dist/assets/{config-BAnYSdpO.js → config-D_c_6ce1.js} +1 -1
- package/dist/assets/{datasource-BLHGFSsT.js → datasource-Tst92Pzi.js} +1 -1
- package/dist/assets/{dependency-graph-panel-C8X3Nn8W.js → dependency-graph-panel-BwMayTyZ.js} +1 -1
- package/dist/assets/{documentation-panel-C8wrSKlX.js → documentation-panel-OMUbXgKW.js} +1 -1
- package/dist/assets/{download-CUgiCx7s.js → download-CPMAf7re.js} +1 -1
- package/dist/assets/{edit-page-CqPNBpAP.js → edit-page-DD1NFkiu.js} +6 -6
- package/dist/assets/{error-entries-DQD4A9cx.js → error-entries-DBpwg0GC.js} +1 -1
- package/dist/assets/{error-panel-BJYkwYEY.js → error-panel-ByOEJuZN.js} +1 -1
- package/dist/assets/{feedback-button-DbTjGwcX.js → feedback-button-7KY7nL-j.js} +2 -2
- package/dist/assets/feedback-button-DxjoRfo3.js +1 -0
- package/dist/assets/{file-explorer-panel-BQi88S1o.js → file-explorer-panel-B92ugwxr.js} +1 -1
- package/dist/assets/{file-name-input-DPJSxW_X.js → file-name-input-DZehOHLA.js} +1 -1
- package/dist/assets/{floating-outline-3PlTr0K1.js → floating-outline-K7cVdOlC.js} +1 -1
- package/dist/assets/{gallery-page-BqvVmwde.js → gallery-page-DoriMrLH.js} +1 -1
- package/dist/assets/{glide-data-editor-CAa14cIr.js → glide-data-editor-DWyeHe3u.js} +1 -1
- package/dist/assets/{home-page-Bg-WqjiD.js → home-page-BcJJsD_Q.js} +1 -1
- package/dist/assets/{hooks-DLBvBo8c.js → hooks-DNPsQf79.js} +1 -1
- package/dist/assets/index-B7ZZVUcj.css +2 -0
- package/dist/assets/{index-BtV91sqJ.js → index-CqJKdR1j.js} +5 -5
- package/dist/assets/{layout-mWXZofX2.js → layout-BaluekG6.js} +3 -3
- package/dist/assets/{logs-panel-IKL6F2OX.js → logs-panel-OkoO5mbN.js} +1 -1
- package/dist/assets/{markdown-renderer-DHvAXF8E.js → markdown-renderer-mDlWBE1M.js} +1 -1
- package/dist/assets/{name-cell-input-j4eGLLyd.js → name-cell-input-D_Q2IhP9.js} +1 -1
- package/dist/assets/{outline-panel-CprDWmxn.js → outline-panel-Dy2cmYDF.js} +1 -1
- package/dist/assets/packages-panel-CIvhx0hb.js +1 -0
- package/dist/assets/{panel-accordion-state-8xXVhawu.js → panel-accordion-state-BI4SU22L.js} +1 -1
- package/dist/assets/panels-CO6Clgel.js +4 -0
- package/dist/assets/{process-output-DOmeJ1VR.js → process-output-Crm0vqbi.js} +1 -1
- package/dist/assets/{readonly-code-display-xvH6MyT6.js → readonly-code-display-CNbw6v1-.js} +1 -1
- package/dist/assets/{readonly-python-code-D4YGmSvB.js → readonly-python-code-DcYhdd1B.js} +1 -1
- package/dist/assets/{reveal-component-CDUG1x74.js → reveal-component-DeZTBbnn.js} +1 -1
- package/dist/assets/{run-page-BAqLgcq9.js → run-page-BNEg-Ro9.js} +1 -1
- package/dist/assets/runtime-details-state-lxXYwJcI.js +1 -0
- package/dist/assets/{scratchpad-panel-BFbUuzdn.js → scratchpad-panel-lDJWcpVs.js} +1 -1
- package/dist/assets/{session-panel-BdN43Lzi.js → session-panel-BqutyoYd.js} +1 -1
- package/dist/assets/{snippets-panel-5_jR08Zd.js → snippets-panel-YttkYAW9.js} +1 -1
- package/dist/assets/state-B2ZbImvm.js +1 -0
- package/dist/assets/{state-BxJ_FX3k.js → state-B85OOscw.js} +1 -1
- package/dist/assets/{state-7os84dnI.js → state-sbCCk7ZP.js} +1 -1
- package/dist/assets/{terminal-DHWTOXu3.js → terminal-3hxM6K9q.js} +1 -1
- package/dist/assets/{tracing-CRkNpBaH.js → tracing-DZYfCw6p.js} +1 -1
- package/dist/assets/{tracing-panel-DV2-2TUI.js → tracing-panel-BUPJ77jB.js} +2 -2
- package/dist/assets/{tree-actions-CShqSonp.js → tree-actions-D5clvgKl.js} +1 -1
- package/dist/assets/{useAddCell-DesMfnE4.js → useAddCell-Bhv6GT3m.js} +1 -1
- package/dist/assets/{useBoolean-BofUVeKg.js → useBoolean-Co-_MyFz.js} +1 -1
- package/dist/assets/{useCellActionButton-BO5SFMxA.js → useCellActionButton-DwAUf_Cn.js} +1 -1
- package/dist/assets/{useDeleteCell-DSbUjpSC.js → useDeleteCell-BKN7t4DH.js} +1 -1
- package/dist/assets/{useDependencyPanelTab-JIFiQrr2.js → useDependencyPanelTab-CbcGEmqM.js} +1 -1
- package/dist/assets/{useInstallPackage-CCQcFc6U.js → useInstallPackage-HTUlGg0U.js} +1 -1
- package/dist/assets/{useNotebookActions-CjjED3t-.js → useNotebookActions-pXzVjrlk.js} +1 -1
- package/dist/assets/{useRunCells-Bx3wXUWO.js → useRunCells-zMO43eNR.js} +1 -1
- package/dist/assets/{useSplitCell-BRmQP02b.js → useSplitCell-xEtFLByO.js} +1 -1
- package/dist/assets/{vega-component-C28fL5fD.js → vega-component-Chbfi__Q.js} +1 -1
- package/dist/index.html +24 -24
- package/package.json +1 -1
- package/src/__tests__/mount.test.ts +6 -6
- package/src/components/editor/actions/useRestartKernel.tsx +2 -2
- package/src/components/editor/alerts/__tests__/connection-notice.test.tsx +224 -17
- package/src/components/editor/alerts/connection-details.tsx +61 -0
- package/src/components/editor/alerts/connection-notice.tsx +46 -53
- package/src/components/editor/alerts/startup-progress.tsx +73 -129
- package/src/components/editor/app-container.tsx +2 -2
- package/src/components/editor/boundary/__tests__/ErrorBoundary.test.tsx +2 -2
- package/src/components/editor/cell/CreateCellButton.tsx +2 -2
- package/src/components/editor/cell/DeleteButton.tsx +2 -2
- package/src/components/editor/cell/RunButton.tsx +3 -3
- package/src/components/editor/cell/StopButton.tsx +2 -2
- package/src/components/editor/chrome/components/__tests__/feedback-button.test.tsx +4 -4
- package/src/components/editor/chrome/components/feedback-button.tsx +3 -3
- package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +8 -8
- package/src/components/editor/chrome/panels/__tests__/{sandbox-details.test.tsx → runtime-details.test.tsx} +94 -22
- package/src/components/editor/chrome/panels/__tests__/sandbox-recovery.test.tsx +4 -4
- package/src/components/editor/chrome/panels/packages-panel.tsx +8 -29
- package/src/components/editor/chrome/panels/{sandbox-details-state.ts → runtime-details-state.ts} +1 -1
- package/src/components/editor/chrome/panels/{sandbox-panel.tsx → runtime-panel.tsx} +38 -66
- package/src/components/editor/chrome/panels/runtime-status-toggle.tsx +56 -0
- package/src/components/editor/chrome/panels/sandbox-controller.tsx +2 -2
- package/src/components/editor/chrome/wrapper/app-chrome.tsx +3 -3
- package/src/components/editor/chrome/wrapper/footer-items/machine-stats.tsx +2 -2
- package/src/components/editor/controls/Controls.tsx +3 -3
- package/src/components/editor/controls/__tests__/notebook-menu-dropdown.test.tsx +2 -2
- package/src/components/editor/header/__tests__/status.test.tsx +4 -4
- package/src/components/editor/header/app-header.tsx +2 -2
- package/src/components/editor/header/status.tsx +3 -3
- package/src/components/ui/progress-stages.tsx +114 -0
- package/src/core/codemirror/lsp/__tests__/notebook-lsp.test.ts +106 -5
- package/src/core/codemirror/lsp/notebook-lsp.ts +16 -2
- package/src/core/codemirror/readonly/__tests__/extension.test.ts +6 -6
- package/src/core/codemirror/readonly/extension.ts +2 -2
- package/src/core/datasets/__tests__/data-source-discovery.test.ts +2 -2
- package/src/core/edit-app.tsx +5 -1
- package/src/core/export/hooks.ts +2 -2
- package/src/core/network/__tests__/connection.test.ts +3 -3
- package/src/core/network/connection-notice.ts +5 -5
- package/src/core/network/connection.ts +9 -9
- package/src/core/network/requests-lazy.ts +2 -2
- package/src/core/packages/__tests__/useSandboxController.test.tsx +2 -2
- package/src/core/packages/useSandboxController.ts +6 -6
- package/src/core/runtime/__tests__/adapter.test.ts +6 -6
- package/src/core/runtime/adapter.ts +7 -7
- package/src/core/runtime/config.ts +2 -2
- package/src/core/saving/filename.ts +2 -2
- package/src/core/saving/save-component.tsx +5 -5
- package/src/core/saving/useAutoSave.ts +2 -2
- package/src/core/websocket/__tests__/useMarimoKernelConnection.hook.test.tsx +30 -25
- package/src/core/websocket/__tests__/useMarimoKernelConnection.test.ts +5 -5
- package/src/core/websocket/connection-utils.ts +21 -21
- package/src/core/websocket/types.ts +8 -9
- package/src/core/websocket/useMarimoKernelConnection.tsx +21 -21
- package/src/mount.tsx +2 -2
- package/src/plugins/core/__test__/registerReactComponent.test.ts +55 -0
- package/src/plugins/core/registerReactComponent.tsx +3 -1
- package/src/stories/cell.stories.tsx +2 -2
- package/src/stories/layout/vertical/one-column.stories.tsx +5 -5
- package/src/stories/progress-stages.stories.tsx +62 -0
- package/src/stories/runtime-existing.stories.tsx +44 -0
- package/src/stories/runtime-maintenance.stories.tsx +40 -0
- package/src/stories/runtime-pixi.stories.tsx +28 -0
- package/src/stories/{sandbox.stories.tsx → runtime-story.tsx} +191 -152
- package/src/stories/runtime-uv.stories.tsx +56 -0
- package/dist/assets/cell-link-BeZAiE1D.js +0 -1
- package/dist/assets/code-visibility-B5QjA0ie.js +0 -1
- package/dist/assets/feedback-button-JqvnTSfN.js +0 -1
- package/dist/assets/index-CF8Gfz1R.css +0 -2
- package/dist/assets/packages-panel-CDDChziK.js +0 -1
- package/dist/assets/panels-CU7deLqc.js +0 -4
- package/dist/assets/sandbox-panel-D5bRunTM.js +0 -1
- package/dist/assets/state-Bo8h64Rn.js +0 -1
- 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 {
|
|
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:
|
|
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 {
|
|
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:
|
|
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 {
|
|
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 ===
|
|
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 ===
|
|
95
|
-
value.state ===
|
|
94
|
+
value.state === ConnectionState.OPEN ||
|
|
95
|
+
value.state === ConnectionState.CLOSED,
|
|
96
96
|
);
|
|
97
|
-
if (status.state ===
|
|
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 ===
|
|
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 {
|
|
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:
|
|
53
|
+
[{ state: ConnectionState.OPEN }, { kind: "ready" }],
|
|
54
54
|
[
|
|
55
|
-
{ state:
|
|
55
|
+
{ state: ConnectionState.CONNECTING },
|
|
56
56
|
{ kind: "connecting", progress: { label: "Connecting…" } },
|
|
57
57
|
],
|
|
58
58
|
[
|
|
59
|
-
{ state:
|
|
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:
|
|
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:
|
|
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 {
|
|
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
|
|
79
|
+
case ConnectionState.OPEN:
|
|
80
80
|
return { kind: "ready" };
|
|
81
|
-
case
|
|
81
|
+
case ConnectionState.CONNECTING:
|
|
82
82
|
return { kind: "connecting", progress: { label: "Connecting…" } };
|
|
83
|
-
case
|
|
83
|
+
case ConnectionState.NOT_STARTED:
|
|
84
84
|
return { kind: "connecting", progress: { label: "Not connected" } };
|
|
85
|
-
case
|
|
86
|
-
case
|
|
85
|
+
case ConnectionState.CLOSING:
|
|
86
|
+
case ConnectionState.CLOSED:
|
|
87
87
|
return {
|
|
88
88
|
kind: "failed",
|
|
89
89
|
error: {
|
|
90
90
|
message:
|
|
91
|
-
conn.state ===
|
|
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 {
|
|
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:
|
|
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 {
|
|
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 !==
|
|
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 {
|
|
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 ===
|
|
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 !==
|
|
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 ===
|
|
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 !==
|
|
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,
|
|
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 ===
|
|
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,
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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(
|
|
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(
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
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(
|
|
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(
|
|
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,
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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 {
|
|
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:
|
|
8
|
-
return state ===
|
|
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:
|
|
15
|
-
return state ===
|
|
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:
|
|
22
|
-
return state ===
|
|
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:
|
|
29
|
-
return state ===
|
|
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:
|
|
36
|
-
return state ===
|
|
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:
|
|
42
|
+
export function isAppInteractionDisabled(state: ConnectionState): boolean {
|
|
43
43
|
return (
|
|
44
|
-
state ===
|
|
45
|
-
state ===
|
|
46
|
-
state ===
|
|
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:
|
|
53
|
+
export function getConnectionTooltip(state: ConnectionState): string {
|
|
54
54
|
switch (state) {
|
|
55
|
-
case
|
|
55
|
+
case ConnectionState.CLOSED:
|
|
56
56
|
return "App disconnected";
|
|
57
|
-
case
|
|
57
|
+
case ConnectionState.CONNECTING:
|
|
58
58
|
return "Connecting to a runtime ...";
|
|
59
|
-
case
|
|
59
|
+
case ConnectionState.CLOSING:
|
|
60
60
|
return "App disconnecting...";
|
|
61
|
-
case
|
|
61
|
+
case ConnectionState.OPEN:
|
|
62
62
|
return "";
|
|
63
|
-
case
|
|
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
|
-
|
|
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
|
|
19
|
-
(typeof
|
|
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
|
|
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
|
|
39
|
+
state: typeof ConnectionState.CONNECTING;
|
|
41
40
|
phase?: ConnectionPhase;
|
|
42
41
|
}
|
|
43
42
|
| {
|
|
44
43
|
state:
|
|
45
|
-
| typeof
|
|
46
|
-
| typeof
|
|
47
|
-
| typeof
|
|
44
|
+
| typeof ConnectionState.OPEN
|
|
45
|
+
| typeof ConnectionState.CLOSING
|
|
46
|
+
| typeof ConnectionState.NOT_STARTED;
|
|
48
47
|
};
|