@marimo-team/islands 0.25.3-dev143 → 0.25.3-dev145

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 (124) hide show
  1. package/dist/{ErrorBoundary-BRBKqg2U.js → ErrorBoundary-wSOY07qE.js} +1 -1
  2. package/dist/{code-visibility-CAgcNzoX.js → code-visibility-ClBVaJxO.js} +1 -1
  3. package/dist/{common-BovIV1xq.js → common-CUexlJOX.js} +5147 -5143
  4. package/dist/{data-grid-overlay-editor-CBsr8Yo0.js → data-grid-overlay-editor-Bw6eRAP6.js} +1 -1
  5. package/dist/{feedback-button-B_9o6DdV.js → feedback-button-DXCqy1zU.js} +2 -2
  6. package/dist/{glide-data-editor-i7BEAfhs.js → glide-data-editor-C5McBx2B.js} +3 -3
  7. package/dist/{globals-BDzA_Re9.js → globals-axkELZAO.js} +1 -1
  8. package/dist/main.js +6 -6
  9. package/dist/{number-overlay-editor-D9WjQlOL.js → number-overlay-editor-bp_Nz8bW.js} +1 -1
  10. package/dist/{reveal-component-BxP2jlPx.js → reveal-component-FoSlb0Gk.js} +2 -2
  11. package/package.json +1 -1
  12. package/src/__tests__/module-entry.test.ts +22 -10
  13. package/src/__tests__/mount.test.ts +1 -1
  14. package/src/__tests__/{host-imports.test.ts → replica-imports.test.ts} +11 -11
  15. package/src/{core/attachments/__tests__/toast.test.tsx → components/editor/__tests__/agent-toast.test.tsx} +1 -1
  16. package/src/components/editor/actions/useRestartKernel.tsx +1 -1
  17. package/src/components/editor/alerts/__tests__/connection-notice.test.tsx +1 -1
  18. package/src/components/editor/alerts/connection-notice.tsx +1 -1
  19. package/src/components/editor/app-container.tsx +1 -1
  20. package/src/components/editor/boundary/__tests__/ErrorBoundary.test.tsx +1 -1
  21. package/src/components/editor/cell/CreateCellButton.tsx +2 -2
  22. package/src/components/editor/cell/DeleteButton.tsx +2 -2
  23. package/src/components/editor/cell/RunButton.tsx +2 -2
  24. package/src/components/editor/cell/StopButton.tsx +2 -2
  25. package/src/components/editor/cell/code/cell-editor.tsx +1 -1
  26. package/src/components/editor/chrome/components/__tests__/feedback-button.test.tsx +1 -1
  27. package/src/components/editor/chrome/components/feedback-button.tsx +1 -1
  28. package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +1 -1
  29. package/src/components/editor/chrome/panels/__tests__/runtime-details.test.tsx +1 -1
  30. package/src/components/editor/chrome/panels/__tests__/sandbox-recovery.test.tsx +1 -1
  31. package/src/components/editor/chrome/wrapper/__tests__/attachment-status.test.tsx +6 -9
  32. package/src/components/editor/chrome/wrapper/footer-items/attachment-status.tsx +4 -4
  33. package/src/components/editor/chrome/wrapper/footer-items/backend-status.tsx +1 -1
  34. package/src/components/editor/chrome/wrapper/footer-items/lsp-status.tsx +1 -1
  35. package/src/components/editor/chrome/wrapper/footer-items/machine-stats.tsx +1 -1
  36. package/src/components/editor/controls/Controls.tsx +2 -2
  37. package/src/components/editor/controls/__tests__/notebook-menu-dropdown.test.tsx +1 -1
  38. package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +6 -9
  39. package/src/components/editor/errors/send-error-report-button.tsx +3 -6
  40. package/src/components/editor/header/__tests__/status.test.tsx +5 -3
  41. package/src/components/editor/header/app-header.tsx +1 -1
  42. package/src/components/editor/header/status.tsx +12 -15
  43. package/src/components/editor/notebook-cell.tsx +1 -1
  44. package/src/core/codemirror/readonly/__tests__/extension.test.ts +1 -1
  45. package/src/core/codemirror/readonly/extension.ts +2 -2
  46. package/src/core/datasets/__tests__/data-source-discovery.test.ts +1 -1
  47. package/src/core/edit-app.tsx +2 -2
  48. package/src/core/export/hooks.ts +1 -1
  49. package/src/core/network/__tests__/connection.test.ts +1 -1
  50. package/src/core/network/connection-notice.ts +1 -1
  51. package/src/core/network/connection.ts +1 -1
  52. package/src/core/network/requests-lazy.ts +1 -1
  53. package/src/core/notebook/attachments.ts +38 -0
  54. package/src/core/{document → notebook/document}/__tests__/codemirror.test.ts +7 -7
  55. package/src/core/{document → notebook/document}/__tests__/fixtures.ts +1 -1
  56. package/src/core/{document → notebook/document}/__tests__/hooks.test.tsx +43 -38
  57. package/src/core/{document → notebook/document}/__tests__/model.test.ts +3 -3
  58. package/src/core/{document → notebook/document}/__tests__/protocol-fixtures.ts +3 -3
  59. package/src/core/{document → notebook/document}/__tests__/scenario-fixture.ts +1 -1
  60. package/src/core/{document → notebook/document}/__tests__/state.test.ts +1 -1
  61. package/src/core/{document → notebook/document}/__tests__/submission.test.ts +1 -1
  62. package/src/core/{document → notebook/document}/__tests__/test-host.ts +1 -1
  63. package/src/core/{document → notebook/document}/__tests__/test-notebook.ts +2 -2
  64. package/src/core/{document → notebook/document}/codemirror.ts +3 -3
  65. package/src/core/{document → notebook/document}/draft.ts +1 -1
  66. package/src/core/{document → notebook/document}/hooks.tsx +9 -9
  67. package/src/core/{document → notebook/document}/index.ts +7 -7
  68. package/src/core/{document → notebook/document}/model.ts +1 -1
  69. package/src/core/{document → notebook/document}/state.ts +1 -1
  70. package/src/core/{document → notebook/document}/sync.ts +1 -1
  71. package/src/core/notebook/index.ts +18 -0
  72. package/src/core/notebook/replica/AGENTS.md +11 -0
  73. package/src/core/{host → notebook/replica}/__tests__/fixtures.ts +1 -1
  74. package/src/core/{host → notebook/replica}/__tests__/replica.test.ts +1 -1
  75. package/src/core/{host → notebook/replica}/index.ts +1 -2
  76. package/src/core/packages/__tests__/useSandboxController.test.tsx +1 -1
  77. package/src/core/packages/useSandboxController.ts +1 -1
  78. package/src/core/run-app.tsx +3 -3
  79. package/src/core/runtime/__tests__/adapter.test.ts +1 -1
  80. package/src/core/runtime/adapter.ts +1 -1
  81. package/src/core/runtime/config.ts +2 -2
  82. package/src/core/saving/filename.ts +1 -1
  83. package/src/core/saving/save-component.tsx +1 -1
  84. package/src/core/saving/useAutoSave.ts +1 -1
  85. package/src/core/{websocket → session}/__tests__/backend.test.ts +3 -3
  86. package/src/core/{websocket → session}/__tests__/useNotebookReplica.hook.test.tsx +49 -7
  87. package/src/core/{websocket → session}/__tests__/useNotebookSession.hook.test.tsx +2 -2
  88. package/src/core/{websocket/state.ts → session/active-notebook.ts} +2 -2
  89. package/src/core/{websocket → session}/backend.ts +3 -7
  90. package/src/core/{websocket → session}/types.ts +2 -2
  91. package/src/core/{websocket → session}/useNotebookReplica.ts +8 -5
  92. package/src/core/{websocket → session}/useNotebookSession.tsx +1 -1
  93. package/src/core/{websocket → transports}/__tests__/connection-state.test.ts +1 -1
  94. package/src/core/{websocket/transports/__tests__/notebook.test.ts → transports/__tests__/server.test.ts} +7 -4
  95. package/src/core/{host → transports}/__tests__/static.test.ts +2 -2
  96. package/src/core/{host → transports}/__tests__/wasm.test.ts +1 -1
  97. package/src/core/{websocket → transports}/connection-state.ts +9 -6
  98. package/src/core/{websocket/transports/notebook.ts → transports/server.ts} +1 -8
  99. package/src/core/{host → transports}/static.ts +9 -2
  100. package/src/core/{host → transports}/wasm.ts +1 -1
  101. package/src/core/wasm/__tests__/bridge.test.ts +8 -8
  102. package/src/core/wasm/bridge.ts +3 -3
  103. package/src/mount.tsx +1 -1
  104. package/src/stories/cell.stories.tsx +1 -1
  105. package/src/stories/layout/vertical/one-column.stories.tsx +1 -1
  106. package/src/stories/runtime-story.tsx +1 -1
  107. package/src/core/attachments/state.ts +0 -18
  108. package/src/core/host/AGENTS.md +0 -12
  109. /package/src/{core/attachments/toast.ts → components/editor/agent-toast.ts} +0 -0
  110. /package/src/core/{document → notebook/document}/__tests__/draft.test.ts +0 -0
  111. /package/src/core/{document → notebook/document}/__tests__/lifecycle-scenarios.test.ts +0 -0
  112. /package/src/core/{document → notebook/document}/__tests__/recovery-scenarios.test.ts +0 -0
  113. /package/src/core/{document → notebook/document}/__tests__/scenarios.test.ts +0 -0
  114. /package/src/core/{document → notebook/document}/__tests__/test-host.test.ts +0 -0
  115. /package/src/core/{document → notebook/document}/__tests__/user.ts +0 -0
  116. /package/src/core/{document → notebook/document}/select.ts +0 -0
  117. /package/src/core/{host → notebook/replica}/__tests__/attachments.test.ts +0 -0
  118. /package/src/core/{host → notebook/replica}/__tests__/document.test.ts +0 -0
  119. /package/src/core/{host → notebook/replica}/backend.ts +0 -0
  120. /package/src/core/{host → notebook/replica}/connection.ts +0 -0
  121. /package/src/core/{host → notebook/replica}/replica.ts +0 -0
  122. /package/src/core/{host → notebook/replica}/resources/attachments.ts +0 -0
  123. /package/src/core/{host → notebook/replica}/resources/document.ts +0 -0
  124. /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 useCellOrder() {
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 useCellCode(id: CellId) {
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 useCellName(id: CellId) {
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 useCellConfig(id: CellId) {
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 = useCellCodeExtension(cellId);
84
+ * const extension = useNotebookCellCodeExtension(cellId);
85
85
  * ```
86
86
  */
87
- export function useCellCodeExtension(id: CellId) {
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 = useDocumentEdit();
100
+ * const edit = useNotebookDocumentEdit();
101
101
  * edit((tx) => tx.setCode(cellId, code));
102
102
  * ```
103
103
  */
104
- export function useDocumentEdit() {
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 useDocumentSync() {
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
- useCellCode,
8
- useCellCodeExtension,
9
- useCellConfig,
10
- useCellName,
11
- useCellOrder,
12
- useDocumentEdit,
13
- useDocumentSync,
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 "../host";
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
 
@@ -6,7 +6,7 @@ import {
6
6
  type createStore,
7
7
  type WritableAtom,
8
8
  } from "jotai/vanilla";
9
- import type { Document } from "../host";
9
+ import type { Document } from "../replica";
10
10
  import * as Protocol from "@marimo-team/client/protocol";
11
11
  import {
12
12
  acknowledge,
@@ -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 "../host";
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
- `../../../../../protocols/host/fixtures/${name}`,
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 "../../websocket/types";
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
- /** Notebook backends and the replica that follows them. */
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/websocket/types";
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/websocket/types";
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 {
@@ -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 "./websocket/connection-utils";
25
- import { useNotebookSession } from "./websocket/useNotebookSession";
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 "../../websocket/types";
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 "../websocket/types";
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 "../websocket/connection-utils";
8
- import { ConnectionState } from "../websocket/types";
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 "../websocket/types";
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 "../websocket/types";
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 "../websocket/types";
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 "../../host/connection";
7
+ import { ConnectionState } from "../../notebook/replica/connection";
8
8
  import {
9
9
  type FakeEdit,
10
10
  type FakeSave,
11
11
  fakeBackend,
12
- } from "../../host/__tests__/fixtures";
13
- import { createNotebookReplica } from "../../host/replica";
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 { fakeBackend, fixtureMessages } from "../../host/__tests__/fixtures";
13
- import { agentAttachmentsAtom } from "../../attachments/state";
14
- import { notebookReplicaAtom } from "../state";
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(notebookReplicaAtom)).toBe(result.current);
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(store.get(agentAttachmentsAtom)).toEqual([
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(notebookReplicaAtom)).toBeNull();
96
- expect(store.get(agentAttachmentsAtom)).toEqual([]);
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 "../connection-state";
53
+ import { classifyCloseEvent } from "../../transports/connection-state";
54
54
  import { createEditorBackend } from "../backend";
55
- import { fakeBackend } from "../../host/__tests__/fixtures";
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 "../host";
4
+ import type { NotebookReplica } from "../notebook/replica";
5
5
 
6
6
  /** The replica owned by the mounted notebook session. */
7
- export const notebookReplicaAtom = atom<NotebookReplica | null>(null);
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 "../host";
7
+ import type { NotebookBackend } from "../notebook/replica";
8
8
  import { PyodideBridge } from "../wasm/bridge";
9
9
  import { isWasm } from "../wasm/utils";
10
- import {
11
- idle,
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 "../host";
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 "../host";
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 { createNotebookReplica, type NotebookReplica } from "../host";
13
- import { notebookReplicaAtom } from "./state";
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(notebookReplicaAtom, replica);
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(notebookReplicaAtom) === replica) {
63
- store.set(notebookReplicaAtom, null);
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/websocket/useNotebookReplica";
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 "../types";
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 "../../../host/replica";
11
- import { fakeBackend, fixtureMessages } from "../../../host/__tests__/fixtures";
12
- import { ConnectionState } from "../../../host/connection";
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 "../notebook";
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 "../resources/document";
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 "./fixtures";
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 "./fixtures";
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 type { CloseDecision } from "../host";
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: WebSocketClosedReason.KERNEL_DISCONNECTED,
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: WebSocketClosedReason.KERNEL_DISCONNECTED,
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: WebSocketClosedReason.KERNEL_DISCONNECTED,
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: WebSocketClosedReason.KERNEL_STARTUP_ERROR,
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 "../connection-state";
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
- });