@marimo-team/islands 0.25.3-dev143 → 0.25.3-dev144
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/{ErrorBoundary-BRBKqg2U.js → ErrorBoundary-h1USoT64.js} +1 -1
- package/dist/{code-visibility-CAgcNzoX.js → code-visibility-COfR8EW1.js} +1 -1
- package/dist/{common-BovIV1xq.js → common-0YnRj3Nk.js} +5147 -5143
- package/dist/{data-grid-overlay-editor-CBsr8Yo0.js → data-grid-overlay-editor-D0f9mjF4.js} +1 -1
- package/dist/{feedback-button-B_9o6DdV.js → feedback-button-BoLxN-to.js} +2 -2
- package/dist/{glide-data-editor-i7BEAfhs.js → glide-data-editor-DAFwdDUi.js} +3 -3
- package/dist/{globals-BDzA_Re9.js → globals-Bw3_VBRf.js} +1 -1
- package/dist/main.js +6 -6
- package/dist/{number-overlay-editor-D9WjQlOL.js → number-overlay-editor-BSF_HajG.js} +1 -1
- package/dist/{reveal-component-BxP2jlPx.js → reveal-component-D0kz9zvJ.js} +2 -2
- package/package.json +1 -1
- package/src/__tests__/module-entry.test.ts +22 -10
- package/src/__tests__/mount.test.ts +1 -1
- package/src/__tests__/{host-imports.test.ts → replica-imports.test.ts} +11 -11
- package/src/{core/attachments/__tests__/toast.test.tsx → components/editor/__tests__/agent-toast.test.tsx} +1 -1
- package/src/components/editor/actions/useRestartKernel.tsx +1 -1
- package/src/components/editor/alerts/__tests__/connection-notice.test.tsx +1 -1
- package/src/components/editor/alerts/connection-notice.tsx +1 -1
- package/src/components/editor/app-container.tsx +1 -1
- package/src/components/editor/boundary/__tests__/ErrorBoundary.test.tsx +1 -1
- 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 +2 -2
- package/src/components/editor/cell/StopButton.tsx +2 -2
- package/src/components/editor/cell/code/cell-editor.tsx +1 -1
- package/src/components/editor/chrome/components/__tests__/feedback-button.test.tsx +1 -1
- package/src/components/editor/chrome/components/feedback-button.tsx +1 -1
- package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +1 -1
- package/src/components/editor/chrome/panels/__tests__/runtime-details.test.tsx +1 -1
- package/src/components/editor/chrome/panels/__tests__/sandbox-recovery.test.tsx +1 -1
- package/src/components/editor/chrome/wrapper/__tests__/attachment-status.test.tsx +6 -9
- package/src/components/editor/chrome/wrapper/footer-items/attachment-status.tsx +4 -4
- package/src/components/editor/chrome/wrapper/footer-items/backend-status.tsx +1 -1
- package/src/components/editor/chrome/wrapper/footer-items/lsp-status.tsx +1 -1
- package/src/components/editor/chrome/wrapper/footer-items/machine-stats.tsx +1 -1
- package/src/components/editor/controls/Controls.tsx +2 -2
- package/src/components/editor/controls/__tests__/notebook-menu-dropdown.test.tsx +1 -1
- package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +6 -9
- package/src/components/editor/errors/send-error-report-button.tsx +3 -6
- package/src/components/editor/header/__tests__/status.test.tsx +5 -3
- package/src/components/editor/header/app-header.tsx +1 -1
- package/src/components/editor/header/status.tsx +12 -15
- package/src/components/editor/notebook-cell.tsx +1 -1
- package/src/core/codemirror/readonly/__tests__/extension.test.ts +1 -1
- package/src/core/codemirror/readonly/extension.ts +2 -2
- package/src/core/datasets/__tests__/data-source-discovery.test.ts +1 -1
- package/src/core/edit-app.tsx +2 -2
- package/src/core/export/hooks.ts +1 -1
- package/src/core/network/__tests__/connection.test.ts +1 -1
- package/src/core/network/connection-notice.ts +1 -1
- package/src/core/network/connection.ts +1 -1
- package/src/core/network/requests-lazy.ts +1 -1
- package/src/core/notebook/attachments.ts +38 -0
- package/src/core/{document → notebook/document}/__tests__/codemirror.test.ts +7 -7
- package/src/core/{document → notebook/document}/__tests__/fixtures.ts +1 -1
- package/src/core/{document → notebook/document}/__tests__/hooks.test.tsx +43 -38
- package/src/core/{document → notebook/document}/__tests__/model.test.ts +3 -3
- package/src/core/{document → notebook/document}/__tests__/protocol-fixtures.ts +3 -3
- package/src/core/{document → notebook/document}/__tests__/scenario-fixture.ts +1 -1
- package/src/core/{document → notebook/document}/__tests__/state.test.ts +1 -1
- package/src/core/{document → notebook/document}/__tests__/submission.test.ts +1 -1
- package/src/core/{document → notebook/document}/__tests__/test-host.ts +1 -1
- package/src/core/{document → notebook/document}/__tests__/test-notebook.ts +2 -2
- package/src/core/{document → notebook/document}/codemirror.ts +3 -3
- package/src/core/{document → notebook/document}/draft.ts +1 -1
- package/src/core/{document → notebook/document}/hooks.tsx +9 -9
- package/src/core/{document → notebook/document}/index.ts +7 -7
- package/src/core/{document → notebook/document}/model.ts +1 -1
- package/src/core/{document → notebook/document}/state.ts +1 -1
- package/src/core/{document → notebook/document}/sync.ts +1 -1
- package/src/core/notebook/index.ts +18 -0
- package/src/core/notebook/replica/AGENTS.md +11 -0
- package/src/core/{host → notebook/replica}/__tests__/fixtures.ts +1 -1
- package/src/core/{host → notebook/replica}/__tests__/replica.test.ts +1 -1
- package/src/core/{host → notebook/replica}/index.ts +1 -2
- package/src/core/packages/__tests__/useSandboxController.test.tsx +1 -1
- package/src/core/packages/useSandboxController.ts +1 -1
- package/src/core/run-app.tsx +3 -3
- package/src/core/runtime/__tests__/adapter.test.ts +1 -1
- package/src/core/runtime/adapter.ts +1 -1
- package/src/core/runtime/config.ts +2 -2
- package/src/core/saving/filename.ts +1 -1
- package/src/core/saving/save-component.tsx +1 -1
- package/src/core/saving/useAutoSave.ts +1 -1
- package/src/core/{websocket → session}/__tests__/backend.test.ts +3 -3
- package/src/core/{websocket → session}/__tests__/useNotebookReplica.hook.test.tsx +49 -7
- package/src/core/{websocket → session}/__tests__/useNotebookSession.hook.test.tsx +2 -2
- package/src/core/{websocket/state.ts → session/active-notebook.ts} +2 -2
- package/src/core/{websocket → session}/backend.ts +3 -7
- package/src/core/{websocket → session}/types.ts +2 -2
- package/src/core/{websocket → session}/useNotebookReplica.ts +8 -5
- package/src/core/{websocket → session}/useNotebookSession.tsx +1 -1
- package/src/core/{websocket → transports}/__tests__/connection-state.test.ts +1 -1
- package/src/core/{websocket/transports/__tests__/notebook.test.ts → transports/__tests__/server.test.ts} +7 -4
- package/src/core/{host → transports}/__tests__/static.test.ts +2 -2
- package/src/core/{host → transports}/__tests__/wasm.test.ts +1 -1
- package/src/core/{websocket → transports}/connection-state.ts +9 -6
- package/src/core/{websocket/transports/notebook.ts → transports/server.ts} +1 -8
- package/src/core/{host → transports}/static.ts +9 -2
- package/src/core/{host → transports}/wasm.ts +1 -1
- package/src/core/wasm/__tests__/bridge.test.ts +8 -8
- package/src/core/wasm/bridge.ts +3 -3
- package/src/mount.tsx +1 -1
- package/src/stories/cell.stories.tsx +1 -1
- package/src/stories/layout/vertical/one-column.stories.tsx +1 -1
- package/src/stories/runtime-story.tsx +1 -1
- package/src/core/attachments/state.ts +0 -18
- package/src/core/host/AGENTS.md +0 -12
- /package/src/{core/attachments/toast.ts → components/editor/agent-toast.ts} +0 -0
- /package/src/core/{document → notebook/document}/__tests__/draft.test.ts +0 -0
- /package/src/core/{document → notebook/document}/__tests__/lifecycle-scenarios.test.ts +0 -0
- /package/src/core/{document → notebook/document}/__tests__/recovery-scenarios.test.ts +0 -0
- /package/src/core/{document → notebook/document}/__tests__/scenarios.test.ts +0 -0
- /package/src/core/{document → notebook/document}/__tests__/test-host.test.ts +0 -0
- /package/src/core/{document → notebook/document}/__tests__/user.ts +0 -0
- /package/src/core/{document → notebook/document}/select.ts +0 -0
- /package/src/core/{host → notebook/replica}/__tests__/attachments.test.ts +0 -0
- /package/src/core/{host → notebook/replica}/__tests__/document.test.ts +0 -0
- /package/src/core/{host → notebook/replica}/backend.ts +0 -0
- /package/src/core/{host → notebook/replica}/connection.ts +0 -0
- /package/src/core/{host → notebook/replica}/replica.ts +0 -0
- /package/src/core/{host → notebook/replica}/resources/attachments.ts +0 -0
- /package/src/core/{host → notebook/replica}/resources/document.ts +0 -0
- /package/src/core/{websocket → session}/connection-utils.ts +0 -0
|
@@ -46,7 +46,7 @@ function useDocumentModel() {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
/** Null while the document is unavailable; an empty array for an empty document. */
|
|
49
|
-
export function
|
|
49
|
+
export function useNotebookCellOrder() {
|
|
50
50
|
const { documentAtom } = useDocumentModel();
|
|
51
51
|
return useAtomValue(useMemo(() => selectOrder(documentAtom), [documentAtom]));
|
|
52
52
|
}
|
|
@@ -62,17 +62,17 @@ function useCellField<K extends keyof EditableCell>(id: CellId, field: K) {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
/** Undefined for an absent cell; null when the host withholds its source. */
|
|
65
|
-
export function
|
|
65
|
+
export function useNotebookCellCode(id: CellId) {
|
|
66
66
|
return useCellField(id, "code");
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
/** Undefined when the cell or document is unavailable. */
|
|
70
|
-
export function
|
|
70
|
+
export function useNotebookCellName(id: CellId) {
|
|
71
71
|
return useCellField(id, "name");
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
/** Undefined when the cell or document is unavailable. */
|
|
75
|
-
export function
|
|
75
|
+
export function useNotebookCellConfig(id: CellId) {
|
|
76
76
|
return useCellField(id, "config");
|
|
77
77
|
}
|
|
78
78
|
|
|
@@ -81,10 +81,10 @@ export function useCellConfig(id: CellId) {
|
|
|
81
81
|
* Stable until the cell id or document model changes.
|
|
82
82
|
*
|
|
83
83
|
* ```tsx
|
|
84
|
-
* const extension =
|
|
84
|
+
* const extension = useNotebookCellCodeExtension(cellId);
|
|
85
85
|
* ```
|
|
86
86
|
*/
|
|
87
|
-
export function
|
|
87
|
+
export function useNotebookCellCodeExtension(id: CellId) {
|
|
88
88
|
const model = useDocumentModel();
|
|
89
89
|
const store = useStore();
|
|
90
90
|
return useMemo(
|
|
@@ -97,11 +97,11 @@ export function useCellCodeExtension(id: CellId) {
|
|
|
97
97
|
* A stable edit function. Synchronous callbacks publish atomically or roll back.
|
|
98
98
|
*
|
|
99
99
|
* ```ts
|
|
100
|
-
* const edit =
|
|
100
|
+
* const edit = useNotebookDocumentEdit();
|
|
101
101
|
* edit((tx) => tx.setCode(cellId, code));
|
|
102
102
|
* ```
|
|
103
103
|
*/
|
|
104
|
-
export function
|
|
104
|
+
export function useNotebookDocumentEdit() {
|
|
105
105
|
return useSetAtom(useDocumentModel().editAtom);
|
|
106
106
|
}
|
|
107
107
|
|
|
@@ -111,7 +111,7 @@ export function useDocumentEdit() {
|
|
|
111
111
|
* Rejections retain local edits; retry explicitly resubmits them. Requests
|
|
112
112
|
* with an unknown outcome reuse their original key within the retry window.
|
|
113
113
|
*/
|
|
114
|
-
export function
|
|
114
|
+
export function useNotebookDocumentSync() {
|
|
115
115
|
const model = useDocumentModel();
|
|
116
116
|
const status = useAtomValue(model.syncStatusAtom);
|
|
117
117
|
return { status, flush: model.flush, retry: model.retry };
|
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
|
|
5
5
|
export {
|
|
6
6
|
DocumentProvider,
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
useNotebookCellCode,
|
|
8
|
+
useNotebookCellCodeExtension,
|
|
9
|
+
useNotebookCellConfig,
|
|
10
|
+
useNotebookCellName,
|
|
11
|
+
useNotebookCellOrder,
|
|
12
|
+
useNotebookDocumentEdit,
|
|
13
|
+
useNotebookDocumentSync,
|
|
14
14
|
} from "./hooks";
|
|
15
15
|
export { createDocumentModel, type DocumentModel } from "./model";
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import type * as Protocol from "@marimo-team/client/protocol";
|
|
4
4
|
import type { Atom, createStore } from "jotai/vanilla";
|
|
5
|
-
import type { Document, NotebookBackend } from "../
|
|
5
|
+
import type { Document, NotebookBackend } from "../replica";
|
|
6
6
|
import { createDocumentState, type DocumentState } from "./state";
|
|
7
7
|
import { createDocumentSync, type DocumentSyncStatus } from "./sync";
|
|
8
8
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { idempotencyKey, type Result } from "@marimo-team/client";
|
|
4
4
|
import type * as Protocol from "@marimo-team/client/protocol";
|
|
5
5
|
import { type Atom, atom, type createStore } from "jotai/vanilla";
|
|
6
|
-
import type { Document, NotebookBackend } from "../
|
|
6
|
+
import type { Document, NotebookBackend } from "../replica";
|
|
7
7
|
import type { DocumentState, DocumentSubmission } from "./state";
|
|
8
8
|
|
|
9
9
|
export type DocumentSyncStatus =
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
/** React hooks for the current notebook. */
|
|
4
|
+
|
|
5
|
+
export {
|
|
6
|
+
useNotebookAttachments,
|
|
7
|
+
attachmentLabel,
|
|
8
|
+
type Attachment,
|
|
9
|
+
} from "./attachments";
|
|
10
|
+
export {
|
|
11
|
+
useNotebookCellCode,
|
|
12
|
+
useNotebookCellCodeExtension,
|
|
13
|
+
useNotebookCellConfig,
|
|
14
|
+
useNotebookCellName,
|
|
15
|
+
useNotebookCellOrder,
|
|
16
|
+
useNotebookDocumentEdit,
|
|
17
|
+
useNotebookDocumentSync,
|
|
18
|
+
} from "./document";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Notebook replica
|
|
2
|
+
|
|
3
|
+
Production code may import replica modules, `@marimo-team/client`,
|
|
4
|
+
`jotai/vanilla`, `zod`, and `@/utils/Logger`. Enforced by
|
|
5
|
+
`marimo/replica-imports`. Tests are exempt.
|
|
6
|
+
|
|
7
|
+
Code outside this directory imports it as `@/core/notebook/replica`.
|
|
8
|
+
Enforced by `marimo/module-entry`.
|
|
9
|
+
|
|
10
|
+
The protocol schemas and types live in `@marimo-team/client/protocol`.
|
|
11
|
+
Define interfaces here and inject application behavior through adapters.
|
|
@@ -45,7 +45,7 @@ export function fakeBackend({
|
|
|
45
45
|
/** Read the protocol's portable examples as transport messages. */
|
|
46
46
|
export function fixtureMessages(name: string) {
|
|
47
47
|
const path = new NodeUrl.URL(
|
|
48
|
-
|
|
48
|
+
`../../../../../../protocols/host/fixtures/${name}`,
|
|
49
49
|
import.meta.url,
|
|
50
50
|
);
|
|
51
51
|
return NodeFs.readFileSync(path, "utf8")
|
|
@@ -5,7 +5,7 @@ import { type NotebookTransport, StreamStopped } from "@marimo-team/client";
|
|
|
5
5
|
import { createStore } from "jotai";
|
|
6
6
|
import { afterEach, describe, expect, it, onTestFinished, vi } from "vitest";
|
|
7
7
|
import { Deferred } from "@/utils/Deferred";
|
|
8
|
-
import { ConnectionState } from "
|
|
8
|
+
import { ConnectionState } from "../../../session/types";
|
|
9
9
|
import type { ConnectionStatus } from "../connection";
|
|
10
10
|
import { createNotebookReplica } from "../replica";
|
|
11
11
|
import { fakeBackend, fixtureEvents, fixtureMessages } from "./fixtures";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
|
-
/**
|
|
3
|
+
/** Backend-confirmed notebook state, independent of React and transports. */
|
|
4
4
|
|
|
5
5
|
export type { NotebookBackend } from "./backend";
|
|
6
6
|
export {
|
|
@@ -11,4 +11,3 @@ export {
|
|
|
11
11
|
} from "./connection";
|
|
12
12
|
export { createNotebookReplica, type NotebookReplica } from "./replica";
|
|
13
13
|
export type { Document } from "./resources/document";
|
|
14
|
-
export { createWasmNotebookBackend } from "./wasm";
|
|
@@ -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 { ConnectionState } from "@/core/
|
|
20
|
+
import { ConnectionState } from "@/core/session/types";
|
|
21
21
|
import { HTTPError } from "@/utils/errors";
|
|
22
22
|
import { packageDataVersionAtom } from "../package-data";
|
|
23
23
|
import {
|
|
@@ -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 { ConnectionState } from "@/core/
|
|
11
|
+
import { ConnectionState } from "@/core/session/types";
|
|
12
12
|
import { HTTPError, prettyError } from "@/utils/errors";
|
|
13
13
|
import { invalidatePackageData } from "./package-data";
|
|
14
14
|
import {
|
package/src/core/run-app.tsx
CHANGED
|
@@ -21,8 +21,8 @@ import { RuntimeState } from "./kernel/RuntimeState";
|
|
|
21
21
|
import { getSessionId } from "./kernel/session";
|
|
22
22
|
import { connectionAtom } from "./network/connection";
|
|
23
23
|
import { useRequestClient } from "./network/requests";
|
|
24
|
-
import { isAppConnecting } from "./
|
|
25
|
-
import { useNotebookSession } from "./
|
|
24
|
+
import { isAppConnecting } from "./session/connection-utils";
|
|
25
|
+
import { useNotebookSession } from "./session/useNotebookSession";
|
|
26
26
|
|
|
27
27
|
interface AppProps {
|
|
28
28
|
appConfig: AppConfig;
|
|
@@ -49,7 +49,7 @@ export const RunApp: React.FC<AppProps> = ({ appConfig, hideHeader }) => {
|
|
|
49
49
|
return () => {
|
|
50
50
|
RuntimeState.INSTANCE.stop();
|
|
51
51
|
};
|
|
52
|
-
}, []);
|
|
52
|
+
}, [sendComponentValues]);
|
|
53
53
|
|
|
54
54
|
const { connection, reconnect } = useNotebookSession({
|
|
55
55
|
autoInstantiate: true,
|
|
@@ -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 { ConnectionState } from "../../
|
|
9
|
+
import { ConnectionState } from "../../session/types";
|
|
10
10
|
import {
|
|
11
11
|
canRunCellsAtom,
|
|
12
12
|
islandsAdapter,
|
|
@@ -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 { ConnectionState } from "../
|
|
11
|
+
import { ConnectionState } from "../session/types";
|
|
12
12
|
|
|
13
13
|
export type RuntimeKind = "wasm" | "remote" | "static" | "islands";
|
|
14
14
|
|
|
@@ -4,8 +4,8 @@ import useEvent from "react-use-event-hook";
|
|
|
4
4
|
import { Logger } from "@/utils/Logger";
|
|
5
5
|
import { connectionAtom } from "../network/connection";
|
|
6
6
|
import { store } from "../state/jotai";
|
|
7
|
-
import { isAppNotStarted } from "../
|
|
8
|
-
import { ConnectionState } from "../
|
|
7
|
+
import { isAppNotStarted } from "../session/connection-utils";
|
|
8
|
+
import { ConnectionState } from "../session/types";
|
|
9
9
|
import { RuntimeManager } from "./runtime";
|
|
10
10
|
import type { RuntimeConfig } from "./types";
|
|
11
11
|
|
|
@@ -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 { ConnectionState } from "../
|
|
12
|
+
import { ConnectionState } from "../session/types";
|
|
13
13
|
import { filenameAtom } from "./file-state";
|
|
14
14
|
|
|
15
15
|
export function useFilename() {
|
|
@@ -34,7 +34,7 @@ import { layoutStateAtom } from "../layout/state";
|
|
|
34
34
|
import { kioskModeAtom } from "../mode";
|
|
35
35
|
import { connectionAtom } from "../network/connection";
|
|
36
36
|
import { useRequestClient } from "../network/requests";
|
|
37
|
-
import { ConnectionState } from "../
|
|
37
|
+
import { ConnectionState } from "../session/types";
|
|
38
38
|
import { isWasm } from "../wasm/utils";
|
|
39
39
|
import { filenameAtom } from "./file-state";
|
|
40
40
|
import { useFilename, useUpdateFilename } from "./filename";
|
|
@@ -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, ConnectionState } from "../
|
|
5
|
+
import { type ConnectionStatus, ConnectionState } from "../session/types";
|
|
6
6
|
|
|
7
7
|
export function useAutoSave(opts: {
|
|
8
8
|
codes: string[];
|
|
@@ -4,13 +4,13 @@ import { NotebookEvents, StreamStopped } from "@marimo-team/client";
|
|
|
4
4
|
import { createStore } from "jotai/vanilla";
|
|
5
5
|
import { afterEach, describe, expect, it, onTestFinished, vi } from "vitest";
|
|
6
6
|
import { Deferred } from "@/utils/Deferred";
|
|
7
|
-
import { ConnectionState } from "../../
|
|
7
|
+
import { ConnectionState } from "../../notebook/replica/connection";
|
|
8
8
|
import {
|
|
9
9
|
type FakeEdit,
|
|
10
10
|
type FakeSave,
|
|
11
11
|
fakeBackend,
|
|
12
|
-
} from "../../
|
|
13
|
-
import { createNotebookReplica } from "../../
|
|
12
|
+
} from "../../notebook/replica/__tests__/fixtures";
|
|
13
|
+
import { createNotebookReplica } from "../../notebook/replica/replica";
|
|
14
14
|
import { createEditorBackend } from "../backend";
|
|
15
15
|
|
|
16
16
|
const wasm = vi.hoisted(() => ({ enabled: false, create: vi.fn() }));
|
|
@@ -9,9 +9,12 @@ import type { NotebookTransport } from "@marimo-team/client";
|
|
|
9
9
|
import { ConnectionState } from "../types";
|
|
10
10
|
import { createEditorBackend } from "../backend";
|
|
11
11
|
import { useNotebookReplica } from "../useNotebookReplica";
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
import {
|
|
13
|
+
fakeBackend,
|
|
14
|
+
fixtureMessages,
|
|
15
|
+
} from "../../notebook/replica/__tests__/fixtures";
|
|
16
|
+
import { useNotebookAttachments } from "@/core/notebook";
|
|
17
|
+
import { activeNotebookReplicaAtom } from "../active-notebook";
|
|
15
18
|
|
|
16
19
|
vi.mock("../backend", () => ({ createEditorBackend: vi.fn() }));
|
|
17
20
|
|
|
@@ -75,14 +78,25 @@ describe("useNotebookReplica", () => {
|
|
|
75
78
|
fakeBackend({ transport: fake.transport }),
|
|
76
79
|
);
|
|
77
80
|
const { result, store, unmount } = setup();
|
|
81
|
+
const attachments = renderHook(
|
|
82
|
+
() => ({
|
|
83
|
+
all: useNotebookAttachments(),
|
|
84
|
+
agents: useNotebookAttachments("agent"),
|
|
85
|
+
}),
|
|
86
|
+
{
|
|
87
|
+
wrapper: ({ children }: PropsWithChildren) => (
|
|
88
|
+
<Provider store={store}>{children}</Provider>
|
|
89
|
+
),
|
|
90
|
+
},
|
|
91
|
+
);
|
|
78
92
|
await act(async () => Promise.resolve());
|
|
79
|
-
expect(store.get(
|
|
93
|
+
expect(store.get(activeNotebookReplicaAtom)).toBe(result.current);
|
|
80
94
|
const [connection] = fake.opened;
|
|
81
95
|
act(() => {
|
|
82
96
|
connection.open();
|
|
83
97
|
fixtureMessages("notebook-events.sse").forEach(connection.receive);
|
|
84
98
|
});
|
|
85
|
-
expect(
|
|
99
|
+
expect(attachments.result.current.agents).toEqual([
|
|
86
100
|
{
|
|
87
101
|
id: "a2",
|
|
88
102
|
notebook_id: "n1",
|
|
@@ -91,9 +105,37 @@ describe("useNotebookReplica", () => {
|
|
|
91
105
|
since: "2026-10-01T09:20:03Z",
|
|
92
106
|
},
|
|
93
107
|
]);
|
|
108
|
+
expect(attachments.result.current.all).toEqual(
|
|
109
|
+
store.get(result.current.attachmentsAtom),
|
|
110
|
+
);
|
|
111
|
+
expect(attachments.result.current.all.length).toBeGreaterThan(
|
|
112
|
+
attachments.result.current.agents.length,
|
|
113
|
+
);
|
|
114
|
+
const agents = attachments.result.current.agents;
|
|
115
|
+
const browser = attachments.result.current.all.find(
|
|
116
|
+
(attachment) => attachment.kind === "browser",
|
|
117
|
+
)!;
|
|
118
|
+
act(() =>
|
|
119
|
+
connection.receive({
|
|
120
|
+
event: "attachment",
|
|
121
|
+
data: { attachment: { ...browser, name: "Firefox" } },
|
|
122
|
+
}),
|
|
123
|
+
);
|
|
124
|
+
expect(attachments.result.current.agents).toBe(agents);
|
|
125
|
+
|
|
126
|
+
const renamedAgent = { ...agents[0], name: "Renamed agent" };
|
|
127
|
+
act(() =>
|
|
128
|
+
connection.receive({
|
|
129
|
+
event: "attachment",
|
|
130
|
+
data: { attachment: renamedAgent },
|
|
131
|
+
}),
|
|
132
|
+
);
|
|
133
|
+
expect(attachments.result.current.agents).toEqual([renamedAgent]);
|
|
134
|
+
|
|
94
135
|
unmount();
|
|
95
|
-
expect(store.get(
|
|
96
|
-
expect(
|
|
136
|
+
expect(store.get(activeNotebookReplicaAtom)).toBeNull();
|
|
137
|
+
expect(attachments.result.current).toEqual({ all: [], agents: [] });
|
|
138
|
+
attachments.unmount();
|
|
97
139
|
});
|
|
98
140
|
|
|
99
141
|
it("uses current callbacks and URL providers without reconnecting on a rerender", async () => {
|
|
@@ -50,9 +50,9 @@ import { connectionAtom, startupProgressAtom } from "../../network/connection";
|
|
|
50
50
|
import { generateSessionId } from "../../kernel/session";
|
|
51
51
|
import { RuntimeManager } from "../../runtime/runtime";
|
|
52
52
|
import { WebSocketClosedReason, ConnectionState } from "../types";
|
|
53
|
-
import { classifyCloseEvent } from "
|
|
53
|
+
import { classifyCloseEvent } from "../../transports/connection-state";
|
|
54
54
|
import { createEditorBackend } from "../backend";
|
|
55
|
-
import { fakeBackend } from "../../
|
|
55
|
+
import { fakeBackend } from "../../notebook/replica/__tests__/fixtures";
|
|
56
56
|
import { useNotebookSession } from "../useNotebookSession";
|
|
57
57
|
import { type NotebookTransport, StreamStopped } from "@marimo-team/client";
|
|
58
58
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
3
|
import { atom } from "jotai";
|
|
4
|
-
import type { NotebookReplica } from "../
|
|
4
|
+
import type { NotebookReplica } from "../notebook/replica";
|
|
5
5
|
|
|
6
6
|
/** The replica owned by the mounted notebook session. */
|
|
7
|
-
export const
|
|
7
|
+
export const activeNotebookReplicaAtom = atom<NotebookReplica | null>(null);
|
|
@@ -4,15 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
import { NotebookClient, StreamStopped } from "@marimo-team/client";
|
|
6
6
|
import type { NotebookId } from "@marimo-team/client/protocol";
|
|
7
|
-
import type { NotebookBackend } from "../
|
|
7
|
+
import type { NotebookBackend } from "../notebook/replica";
|
|
8
8
|
import { PyodideBridge } from "../wasm/bridge";
|
|
9
9
|
import { isWasm } from "../wasm/utils";
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
sessionSse,
|
|
13
|
-
websocket,
|
|
14
|
-
withHeaders,
|
|
15
|
-
} from "./transports/notebook";
|
|
10
|
+
import { sessionSse, websocket, withHeaders } from "../transports/server";
|
|
11
|
+
import { idle } from "../transports/static";
|
|
16
12
|
|
|
17
13
|
/** How the editor follows its notebook. */
|
|
18
14
|
export interface EditorBackendOptions {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
|
-
import type { ConnectionStatus as HostConnectionStatus } from "../
|
|
3
|
+
import type { ConnectionStatus as HostConnectionStatus } from "../notebook/replica";
|
|
4
4
|
import type { NotificationMessageData } from "../kernel/messages";
|
|
5
5
|
|
|
6
6
|
export {
|
|
7
7
|
ConnectionState,
|
|
8
8
|
ConnectionClosedReason as WebSocketClosedReason,
|
|
9
|
-
} from "../
|
|
9
|
+
} from "../notebook/replica";
|
|
10
10
|
|
|
11
11
|
export type ConnectionPhase =
|
|
12
12
|
| NotificationMessageData<"startup-progress">["phase"]
|
|
@@ -9,8 +9,11 @@ import type {
|
|
|
9
9
|
NotificationMessage,
|
|
10
10
|
NotificationPayload,
|
|
11
11
|
} from "../kernel/messages";
|
|
12
|
-
import {
|
|
13
|
-
|
|
12
|
+
import {
|
|
13
|
+
createNotebookReplica,
|
|
14
|
+
type NotebookReplica,
|
|
15
|
+
} from "../notebook/replica";
|
|
16
|
+
import { activeNotebookReplicaAtom } from "./active-notebook";
|
|
14
17
|
import { createEditorBackend, type EditorBackendOptions } from "./backend";
|
|
15
18
|
|
|
16
19
|
type Options = EditorBackendOptions & {
|
|
@@ -51,7 +54,7 @@ export function useNotebookReplica(options: Options): NotebookReplica {
|
|
|
51
54
|
);
|
|
52
55
|
|
|
53
56
|
useEffect(() => {
|
|
54
|
-
store.set(
|
|
57
|
+
store.set(activeNotebookReplicaAtom, replica);
|
|
55
58
|
const disconnect = replica.connect(backend, {
|
|
56
59
|
// The host validates the envelope, not the native notification contents.
|
|
57
60
|
onNotification: (notification) =>
|
|
@@ -59,8 +62,8 @@ export function useNotebookReplica(options: Options): NotebookReplica {
|
|
|
59
62
|
});
|
|
60
63
|
return () => {
|
|
61
64
|
disconnect();
|
|
62
|
-
if (store.get(
|
|
63
|
-
store.set(
|
|
65
|
+
if (store.get(activeNotebookReplicaAtom) === replica) {
|
|
66
|
+
store.set(activeNotebookReplicaAtom, null);
|
|
64
67
|
}
|
|
65
68
|
};
|
|
66
69
|
}, [replica, backend, store, onNotification]);
|
|
@@ -12,7 +12,7 @@ import type {
|
|
|
12
12
|
NotificationMessageData,
|
|
13
13
|
NotificationPayload,
|
|
14
14
|
} from "@/core/kernel/messages";
|
|
15
|
-
import { useNotebookReplica } from "@/core/
|
|
15
|
+
import { useNotebookReplica } from "@/core/session/useNotebookReplica";
|
|
16
16
|
import { renderHTML } from "@/plugins/core/RenderHTML";
|
|
17
17
|
import {
|
|
18
18
|
handleWidgetMessage,
|
|
@@ -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, ConnectionState } from "
|
|
5
|
+
import { WebSocketClosedReason, ConnectionState } from "../../session/types";
|
|
6
6
|
import { classifyCloseEvent } from "../connection-state";
|
|
7
7
|
|
|
8
8
|
function classify(reason: string | undefined) {
|
|
@@ -7,11 +7,14 @@ import {
|
|
|
7
7
|
} from "@marimo-team/client";
|
|
8
8
|
import { createStore } from "jotai/vanilla";
|
|
9
9
|
import { afterEach, describe, expect, it, onTestFinished, vi } from "vitest";
|
|
10
|
-
import { createNotebookReplica } from "
|
|
11
|
-
import {
|
|
12
|
-
|
|
10
|
+
import { createNotebookReplica } from "../../notebook/replica/replica";
|
|
11
|
+
import {
|
|
12
|
+
fakeBackend,
|
|
13
|
+
fixtureMessages,
|
|
14
|
+
} from "../../notebook/replica/__tests__/fixtures";
|
|
15
|
+
import { ConnectionState } from "../../notebook/replica/connection";
|
|
13
16
|
import { Deferred } from "@/utils/Deferred";
|
|
14
|
-
import { sessionSse, websocket } from "../
|
|
17
|
+
import { sessionSse, websocket } from "../server";
|
|
15
18
|
|
|
16
19
|
const encoder = new TextEncoder();
|
|
17
20
|
|
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
import { createNotebookReducer, NotebookEvents } from "@marimo-team/client";
|
|
4
4
|
import * as Protocol from "@marimo-team/client/protocol";
|
|
5
5
|
import { afterEach, assert, describe, expect, it, vi } from "vitest";
|
|
6
|
-
import { document as documentResource } from "
|
|
6
|
+
import { document as documentResource } from "../../notebook/replica/resources/document";
|
|
7
7
|
import {
|
|
8
8
|
createStaticNotebookBackend,
|
|
9
9
|
type StaticNotebookSnapshot,
|
|
10
10
|
} from "../static";
|
|
11
|
-
import { fixtureEvents } from "
|
|
11
|
+
import { fixtureEvents } from "../../notebook/replica/__tests__/fixtures";
|
|
12
12
|
|
|
13
13
|
function snapshot(): StaticNotebookSnapshot {
|
|
14
14
|
const events = fixtureEvents("notebook-events.sse");
|
|
@@ -4,7 +4,7 @@ import { NotebookEvents, StreamStopped } from "@marimo-team/client";
|
|
|
4
4
|
import { afterEach, assert, describe, expect, it, vi } from "vitest";
|
|
5
5
|
import { Deferred } from "@/utils/Deferred";
|
|
6
6
|
import { createWasmNotebookBackend, type WasmNotebookBridge } from "../wasm";
|
|
7
|
-
import { fixtureEvents } from "
|
|
7
|
+
import { fixtureEvents } from "../../notebook/replica/__tests__/fixtures";
|
|
8
8
|
|
|
9
9
|
const streams: NotebookEvents[] = [];
|
|
10
10
|
afterEach(async () => {
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import {
|
|
4
|
+
type CloseDecision,
|
|
5
|
+
ConnectionState,
|
|
6
|
+
ConnectionClosedReason,
|
|
7
|
+
} from "../notebook/replica";
|
|
4
8
|
import { Logger } from "@/utils/Logger";
|
|
5
|
-
import { ConnectionState, WebSocketClosedReason } from "./types";
|
|
6
9
|
|
|
7
10
|
// Keep server reasons in sync with ws_endpoint.py and ws/*.py.
|
|
8
11
|
// SSE carries the same reasons in close events. Unclassified reasons retry.
|
|
@@ -16,7 +19,7 @@ export function classifyCloseEvent(event: { reason?: string }): CloseDecision {
|
|
|
16
19
|
kind: "terminal",
|
|
17
20
|
status: {
|
|
18
21
|
state: ConnectionState.CLOSED,
|
|
19
|
-
code:
|
|
22
|
+
code: ConnectionClosedReason.KERNEL_DISCONNECTED,
|
|
20
23
|
reason: "kernel not found",
|
|
21
24
|
},
|
|
22
25
|
};
|
|
@@ -25,7 +28,7 @@ export function classifyCloseEvent(event: { reason?: string }): CloseDecision {
|
|
|
25
28
|
kind: "terminal",
|
|
26
29
|
status: {
|
|
27
30
|
state: ConnectionState.CLOSED,
|
|
28
|
-
code:
|
|
31
|
+
code: ConnectionClosedReason.KERNEL_DISCONNECTED,
|
|
29
32
|
reason: "not authorized",
|
|
30
33
|
},
|
|
31
34
|
};
|
|
@@ -34,7 +37,7 @@ export function classifyCloseEvent(event: { reason?: string }): CloseDecision {
|
|
|
34
37
|
kind: "terminal",
|
|
35
38
|
status: {
|
|
36
39
|
state: ConnectionState.CLOSED,
|
|
37
|
-
code:
|
|
40
|
+
code: ConnectionClosedReason.KERNEL_DISCONNECTED,
|
|
38
41
|
reason: "kiosk mode is not available for this session",
|
|
39
42
|
},
|
|
40
43
|
};
|
|
@@ -43,7 +46,7 @@ export function classifyCloseEvent(event: { reason?: string }): CloseDecision {
|
|
|
43
46
|
kind: "terminal",
|
|
44
47
|
status: {
|
|
45
48
|
state: ConnectionState.CLOSED,
|
|
46
|
-
code:
|
|
49
|
+
code: ConnectionClosedReason.KERNEL_STARTUP_ERROR,
|
|
47
50
|
reason: "Failed to start kernel sandbox",
|
|
48
51
|
},
|
|
49
52
|
};
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
type StreamUrl,
|
|
8
8
|
} from "@marimo-team/client";
|
|
9
9
|
import { jsonParseWithSpecialChar } from "@/utils/json/json-parser";
|
|
10
|
-
import { classifyCloseEvent } from "
|
|
10
|
+
import { classifyCloseEvent } from "./connection-state";
|
|
11
11
|
|
|
12
12
|
/** The server can be slow to accept when many notebooks are open. */
|
|
13
13
|
const CONNECT_MS = 10_000;
|
|
@@ -150,10 +150,3 @@ export function sessionSse(
|
|
|
150
150
|
}),
|
|
151
151
|
);
|
|
152
152
|
}
|
|
153
|
-
|
|
154
|
-
/** A connection that opens and delivers nothing, for static notebooks. */
|
|
155
|
-
export const idle: NotebookTransport = ({ signal }, { open }) =>
|
|
156
|
-
new Promise((settle) => {
|
|
157
|
-
open();
|
|
158
|
-
signal.addEventListener("abort", () => settle(), { once: true });
|
|
159
|
-
});
|