@marimo-team/frontend 0.25.3-dev2 → 0.25.3-dev20

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 (182) hide show
  1. package/dist/assets/{CellStatus-BP2mavAq.js → CellStatus-DVOHpdeJ.js} +1 -1
  2. package/dist/assets/{ConnectedDataExplorerComponent-t8fH_TNc.js → ConnectedDataExplorerComponent-TprnxZeJ.js} +1 -1
  3. package/dist/assets/{ErrorBoundary-Gl5nCA8C.js → ErrorBoundary-CM13wrd8.js} +2 -2
  4. package/dist/assets/JsonOutput-Cx1iyibB.js +11 -0
  5. package/dist/assets/{LazyAnyLanguageCodeMirror-CKcCbG2G.js → LazyAnyLanguageCodeMirror-B4fCknq1.js} +2 -2
  6. package/dist/assets/MarimoErrorOutput-s_5GOX8k.js +10 -0
  7. package/dist/assets/{RenderHTML-BbOqmHy3.js → RenderHTML-DX6i99Op.js} +1 -1
  8. package/dist/assets/{RunButton-CK3LhnP5.js → RunButton-BKoh0zYG.js} +1 -1
  9. package/dist/assets/{add-cell-with-ai-8UBC0xwR.js → add-cell-with-ai-BpD_790A.js} +11 -11
  10. package/dist/assets/{add-connection-dialog-DXQnd6eB.js → add-connection-dialog-B1BXCjex.js} +2 -2
  11. package/dist/assets/{agent-panel-D7kMZLG-.js → agent-panel-CDFUc1uu.js} +5 -5
  12. package/dist/assets/ai-model-dropdown-i1vfGeVi.js +13 -0
  13. package/dist/assets/{any-language-editor-BLCAD3yx.js → any-language-editor-BdTWdMCI.js} +1 -1
  14. package/dist/assets/{app-config-button-BKEG4x9u.js → app-config-button-AdV-QDD7.js} +1 -1
  15. package/dist/assets/{blob-D5nfNHOo.js → blob-BG8yoQmx.js} +1 -1
  16. package/dist/assets/{cell-editor-ADVfycpN.js → cell-editor-BKJ06H5F.js} +9 -9
  17. package/dist/assets/cell-link-8Auigi8P.js +1 -0
  18. package/dist/assets/{cells-BRkCaSsh.js → cells-C6YGYGdi.js} +56 -56
  19. package/dist/assets/{chat-display-Dnagy4Nv.js → chat-display-DhgI2MS9.js} +1 -1
  20. package/dist/assets/{chat-panel-DQ6HkWEF.js → chat-panel-Cau71Ygr.js} +2 -2
  21. package/dist/assets/{chat-ui-Df8Vcj6n.js → chat-ui-C5ql1Up-.js} +1 -1
  22. package/dist/assets/code-visibility-BJImdpDi.js +1 -0
  23. package/dist/assets/coerce-DWjFeXOY.js +1 -0
  24. package/dist/assets/{column-preview-Cm2D6vyX.js → column-preview-BZ_iNhtJ.js} +1 -1
  25. package/dist/assets/{command-palette-DTSQ7gOC.js → command-palette-DDC3kBet.js} +1 -1
  26. package/dist/assets/{common-Cu6CDbuw.js → common-Sl3uthjg.js} +1 -1
  27. package/dist/assets/{components-CGw16R_N.js → components-3cou6Mka.js} +1 -1
  28. package/dist/assets/{components-CP_UoFtT.js → components-CskjK6xZ.js} +1 -1
  29. package/dist/assets/{components-CXfO-F47.js → components-YBmtV0p6.js} +1 -1
  30. package/dist/assets/{config-VPSq73n8.js → config-CA1V-wjh.js} +1 -1
  31. package/dist/assets/config-DRIpOKqh.js +1 -0
  32. package/dist/assets/config-Dy28fDwn.js +1 -0
  33. package/dist/assets/{datasource-Cq6x_5ML.js → datasource-DfbNb83e.js} +1 -1
  34. package/dist/assets/{dates-BbFQ2oE4.js → dates-CfmdTL6B.js} +1 -1
  35. package/dist/assets/{dependency-graph-panel--Kgtktcw.js → dependency-graph-panel-L5cBE5sl.js} +1 -1
  36. package/dist/assets/{documentation-panel-BHmbeznw.js → documentation-panel-D7S8JIc2.js} +1 -1
  37. package/dist/assets/{download-DCJXy66H.js → download-C03FOElq.js} +1 -1
  38. package/dist/assets/edit-page-CLF_VUTW.js +9 -0
  39. package/dist/assets/{error-banner-Cd69vvyX.js → error-banner-COZ7SfYV.js} +1 -1
  40. package/dist/assets/{error-entries-Dk3VZ-OK.js → error-entries-CLnPDv7b.js} +1 -1
  41. package/dist/assets/{error-panel-BPRYmC5J.js → error-panel-D2xrYLue.js} +1 -1
  42. package/dist/assets/{errors-DCrsa1nH.js → errors-CNsgAw6m.js} +1 -1
  43. package/dist/assets/{feedback-button-Bik2-e33.js → feedback-button-DzPunIqU.js} +2 -2
  44. package/dist/assets/feedback-button-Kla7i5RT.js +1 -0
  45. package/dist/assets/{file-explorer-panel-BEEK5lcl.js → file-explorer-panel-Uk4_QIJQ.js} +1 -1
  46. package/dist/assets/{file-name-input-Cdc-TVqO.js → file-name-input-B4fRaQkI.js} +1 -1
  47. package/dist/assets/{floating-outline-XD9FAV-f.js → floating-outline-B5MaM1JS.js} +1 -1
  48. package/dist/assets/{gallery-page-Dm6mGwBi.js → gallery-page-BefvdsM7.js} +1 -1
  49. package/dist/assets/{glide-data-editor-J9CAb70d.js → glide-data-editor-C3eaYlXg.js} +1 -1
  50. package/dist/assets/{home-page-DMHnsRxm.js → home-page-hmvKlc1w.js} +1 -1
  51. package/dist/assets/{hooks-93IwHd2u.js → hooks-DzML9nez.js} +1 -1
  52. package/dist/assets/index-6b4mf8Ai.css +2 -0
  53. package/dist/assets/index-CMx5iSyu.js +30 -0
  54. package/dist/assets/{layout-DmcNEu3P.js → layout-Ck1RoL1B.js} +5 -5
  55. package/dist/assets/{logs-panel-ABM16htT.js → logs-panel-BRytaWaO.js} +1 -1
  56. package/dist/assets/{markdown-renderer-BG3sp2rs.js → markdown-renderer-DX0Xrrfy.js} +1 -1
  57. package/dist/assets/{mermaid-a3xYymTy.js → mermaid-C4EiVqcd.js} +1 -1
  58. package/dist/assets/{name-cell-input-BgEBmqyL.js → name-cell-input-DlpTvPLa.js} +1 -1
  59. package/dist/assets/{outline-panel-DCXQ3_F6.js → outline-panel-B2WIpz-1.js} +1 -1
  60. package/dist/assets/{packages-panel-BMGOmZkR.js → packages-panel-CI5ff9b-.js} +1 -1
  61. package/dist/assets/{panel-accordion-state-C1sdPp0m.js → panel-accordion-state-BqSH5ttk.js} +1 -1
  62. package/dist/assets/panels-B_jlikjE.js +107 -0
  63. package/dist/assets/{process-output-D0KGq04R.js → process-output-f3C3PD0Z.js} +1 -1
  64. package/dist/assets/{readonly-code-display-Do50PHI8.js → readonly-code-display-MnMEoYW5.js} +1 -1
  65. package/dist/assets/{readonly-python-code-DCSMIsTX.js → readonly-python-code-ggs4hAzC.js} +1 -1
  66. package/dist/assets/{renderShortcut-BkVJQ_Zh.js → renderShortcut-C1JwYcqW.js} +1 -1
  67. package/dist/assets/{reveal-component-ltaepAqw.js → reveal-component-PiV1eQk9.js} +1 -1
  68. package/dist/assets/run-page-CkP22x9D.js +1 -0
  69. package/dist/assets/{runtime-details-state-CSPobEdR.js → runtime-details-state-CoL-p0MC.js} +1 -1
  70. package/dist/assets/{schemas-Dv42wLFq.js → schemas-ChRqMOTL.js} +1 -1
  71. package/dist/assets/{scratchpad-panel-Dwd6W5PE.js → scratchpad-panel-D2rWg4k8.js} +1 -1
  72. package/dist/assets/{secrets-panel-Bctbtkrp.js → secrets-panel-D7KN4tVs.js} +1 -1
  73. package/dist/assets/session-panel-CnANhcTo.js +1 -0
  74. package/dist/assets/{snippets-panel-Bh1oTw4m.js → snippets-panel-ClGHtEKQ.js} +1 -1
  75. package/dist/assets/state-CbjBtkdd.js +3 -0
  76. package/dist/assets/{state-XsPO8hNi.js → state-DtGB7xeJ.js} +1 -1
  77. package/dist/assets/{terminal-Ck_vK-34.js → terminal-wnSHYT8N.js} +1 -1
  78. package/dist/assets/{tracing-Di6pUmX8.js → tracing-Dz2rYOFL.js} +1 -1
  79. package/dist/assets/{tracing-panel-PeRXHD8_.js → tracing-panel-DqQY9V91.js} +2 -2
  80. package/dist/assets/{tree-actions-GM5E_bUs.js → tree-actions-DnUokmva.js} +1 -1
  81. package/dist/assets/{useAddCell-GR7eEQrT.js → useAddCell-IN24thff.js} +1 -1
  82. package/dist/assets/{useBoolean-BDD6_xQQ.js → useBoolean-d0Tty94N.js} +1 -1
  83. package/dist/assets/{useCellActionButton-B-9eG5z_.js → useCellActionButton-DmaFZ4oF.js} +1 -1
  84. package/dist/assets/{useDeleteCell-2_bMClHd.js → useDeleteCell-fQlM46Y2.js} +1 -1
  85. package/dist/assets/{useDependencyPanelTab-k3C3M18S.js → useDependencyPanelTab-ImXYINm_.js} +1 -1
  86. package/dist/assets/{useInstallPackage-iX1eufdl.js → useInstallPackage-BW06eter.js} +1 -1
  87. package/dist/assets/{useNotebookActions-NHN6A-cN.js → useNotebookActions-DHt3s-ia.js} +1 -1
  88. package/dist/assets/{useRunCells-B2vUhREg.js → useRunCells-DSJPXE-j.js} +1 -1
  89. package/dist/assets/{useSplitCell-DEb03htp.js → useSplitCell-DQyMF5gd.js} +1 -1
  90. package/dist/assets/{useTheme-COhSHkCj.js → useTheme-n8Dj1zH7.js} +1 -1
  91. package/dist/assets/{vega-component-CnadTgoE.js → vega-component-IouMe13t.js} +1 -1
  92. package/dist/index.html +34 -35
  93. package/package.json +1 -1
  94. package/src/__tests__/fixtures/{pair-preview.ts → pair-config.ts} +3 -2
  95. package/src/__tests__/mount.test.ts +28 -8
  96. package/src/components/data-table/__tests__/context-menu.test.ts +176 -0
  97. package/src/components/data-table/__tests__/date-filter-inputs.test.tsx +24 -1
  98. package/src/components/data-table/__tests__/export-actions.test.tsx +329 -6
  99. package/src/components/data-table/context-menu.tsx +125 -26
  100. package/src/components/data-table/date-filter-inputs.tsx +15 -42
  101. package/src/components/data-table/export-actions.tsx +192 -116
  102. package/src/components/data-table/schemas.ts +14 -36
  103. package/src/components/data-table/utils.ts +18 -0
  104. package/src/components/editor/Output.tsx +9 -2
  105. package/src/components/editor/actions/__tests__/pair-with-agent-commands.test.ts +80 -252
  106. package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +186 -34
  107. package/src/components/editor/actions/pair-with-agent-commands.ts +42 -51
  108. package/src/components/editor/actions/pair-with-agent-modal.tsx +96 -36
  109. package/src/components/editor/chrome/panels/error-panel.tsx +1 -0
  110. package/src/components/editor/chrome/wrapper/__tests__/attachment-status.test.tsx +372 -0
  111. package/src/components/editor/chrome/wrapper/footer-item.tsx +32 -24
  112. package/src/components/editor/chrome/wrapper/footer-items/attachment-status.tsx +237 -0
  113. package/src/components/editor/chrome/wrapper/footer.tsx +2 -0
  114. package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +548 -0
  115. package/src/components/editor/errors/send-error-report-button.tsx +69 -0
  116. package/src/components/editor/output/MarimoErrorOutput.tsx +21 -5
  117. package/src/components/editor/output/MarimoTracebackOutput.tsx +35 -20
  118. package/src/components/editor/output/__tests__/ansi-reduce.test.ts +18 -0
  119. package/src/components/editor/output/ansi-reduce.ts +6 -0
  120. package/src/core/attachments/__tests__/toast.test.tsx +58 -0
  121. package/src/core/attachments/state.ts +21 -0
  122. package/src/core/attachments/toast.ts +15 -0
  123. package/src/core/cells/__tests__/cells.test.ts +52 -0
  124. package/src/core/cells/__tests__/error-report.test.ts +139 -0
  125. package/src/core/cells/__tests__/session.test.ts +50 -1
  126. package/src/core/cells/error-report.ts +63 -0
  127. package/src/core/cells/session.ts +28 -1
  128. package/src/core/config/pair.ts +5 -3
  129. package/src/core/edit-app.tsx +2 -2
  130. package/src/core/host/AGENTS.md +5 -0
  131. package/src/core/host/__tests__/document.test.ts +124 -0
  132. package/src/core/host/__tests__/events.test.ts +46 -0
  133. package/src/core/host/__tests__/fixtures.ts +39 -0
  134. package/src/core/host/__tests__/notebook-client.test.ts +303 -0
  135. package/src/core/host/__tests__/stream.test.ts +211 -0
  136. package/src/core/host/connection.ts +81 -0
  137. package/src/core/host/events.ts +60 -0
  138. package/src/core/host/notebook-client.ts +272 -0
  139. package/src/core/host/resources/document.ts +101 -0
  140. package/src/core/host/resources/resource.ts +59 -0
  141. package/src/core/host/stream.ts +213 -0
  142. package/src/core/islands/__tests__/bootstrap.test.ts +26 -0
  143. package/src/core/islands/bootstrap.ts +1 -0
  144. package/src/core/network/connection.ts +4 -2
  145. package/src/core/run-app.tsx +2 -2
  146. package/src/core/runtime/__tests__/runtime.test.ts +55 -11
  147. package/src/core/runtime/runtime.ts +14 -1
  148. package/src/core/runtime/types.ts +2 -0
  149. package/src/core/websocket/__tests__/{useMarimoKernelConnection.test.ts → connection-state.test.ts} +15 -17
  150. package/src/core/websocket/__tests__/{useWebSocket.test.ts → createConnectionTransport.test.ts} +1 -1
  151. package/src/core/websocket/__tests__/useNotebookClient.hook.test.tsx +140 -0
  152. package/src/core/websocket/__tests__/{useMarimoKernelConnection.hook.test.tsx → useNotebookSession.hook.test.tsx} +352 -65
  153. package/src/core/websocket/connection-state.ts +70 -0
  154. package/src/core/websocket/transports/__tests__/sse.test.ts +1 -4
  155. package/src/core/websocket/transports/create.ts +31 -0
  156. package/src/core/websocket/transports/transport.ts +12 -23
  157. package/src/core/websocket/types.ts +9 -39
  158. package/src/core/websocket/useNotebookClient.ts +80 -0
  159. package/src/core/websocket/{useMarimoKernelConnection.tsx → useNotebookSession.tsx} +67 -225
  160. package/src/mount.tsx +7 -3
  161. package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +84 -0
  162. package/src/utils/__tests__/dates.test.ts +116 -0
  163. package/src/utils/dates.ts +94 -0
  164. package/dist/assets/JsonOutput-D6HIIsCb.js +0 -11
  165. package/dist/assets/MarimoErrorOutput-D60XkCny.js +0 -6
  166. package/dist/assets/ai-model-dropdown-B0z6-zhp.js +0 -5
  167. package/dist/assets/api-BeC_VSGv.js +0 -1
  168. package/dist/assets/cell-link-LSth_8hT.js +0 -1
  169. package/dist/assets/code-visibility-B-_GvwR5.js +0 -1
  170. package/dist/assets/coerce-CpE6kUdX.js +0 -1
  171. package/dist/assets/config-BOKzA3tF.js +0 -1
  172. package/dist/assets/config-DX8Y2WbJ.js +0 -1
  173. package/dist/assets/edit-page-ChcSgpke.js +0 -9
  174. package/dist/assets/feedback-button-B2gKC5cn.js +0 -1
  175. package/dist/assets/index-Bcdzex-3.css +0 -2
  176. package/dist/assets/index-s-M1OXjs.js +0 -29
  177. package/dist/assets/pair-CaQyK6En.js +0 -1
  178. package/dist/assets/panels-CxVG8-v2.js +0 -4
  179. package/dist/assets/run-page-DF3c5cDk.js +0 -1
  180. package/dist/assets/session-panel-VdDhEBa-.js +0 -1
  181. package/dist/assets/state-_Qai_QyX.js +0 -3
  182. package/src/core/websocket/useWebSocket.tsx +0 -88
@@ -0,0 +1,70 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import type { CloseDecision } from "../host/connection";
4
+ import { Logger } from "@/utils/Logger";
5
+ import { TRANSPORT_EXHAUSTED_REASON } from "./transports/ws";
6
+ import { ConnectionState, WebSocketClosedReason } from "./types";
7
+
8
+ // Keep server reasons in sync with ws_endpoint.py and ws/*.py.
9
+ // SSE carries the same reasons in close events. Unclassified reasons retry.
10
+ export function classifyCloseEvent(event: { reason?: string }): CloseDecision {
11
+ switch (event.reason) {
12
+ case TRANSPORT_EXHAUSTED_REASON:
13
+ return {
14
+ kind: "gave-up",
15
+ status: {
16
+ state: ConnectionState.CLOSED,
17
+ code: WebSocketClosedReason.KERNEL_DISCONNECTED,
18
+ reason: "kernel not found",
19
+ },
20
+ };
21
+ case "MARIMO_NO_FILE_KEY":
22
+ case "MARIMO_NO_SESSION_ID":
23
+ case "MARIMO_NO_SESSION":
24
+ case "MARIMO_SHUTDOWN":
25
+ return {
26
+ kind: "terminal",
27
+ status: {
28
+ state: ConnectionState.CLOSED,
29
+ code: WebSocketClosedReason.KERNEL_DISCONNECTED,
30
+ reason: "kernel not found",
31
+ },
32
+ };
33
+ case "MARIMO_UNAUTHORIZED":
34
+ return {
35
+ kind: "terminal",
36
+ status: {
37
+ state: ConnectionState.CLOSED,
38
+ code: WebSocketClosedReason.KERNEL_DISCONNECTED,
39
+ reason: "not authorized",
40
+ },
41
+ };
42
+ case "MARIMO_KIOSK_NOT_ALLOWED":
43
+ return {
44
+ kind: "terminal",
45
+ status: {
46
+ state: ConnectionState.CLOSED,
47
+ code: WebSocketClosedReason.KERNEL_DISCONNECTED,
48
+ reason: "kiosk mode is not available for this session",
49
+ },
50
+ };
51
+ case "MARIMO_KERNEL_STARTUP_ERROR":
52
+ return {
53
+ kind: "terminal",
54
+ status: {
55
+ state: ConnectionState.CLOSED,
56
+ code: WebSocketClosedReason.KERNEL_STARTUP_ERROR,
57
+ reason: "Failed to start kernel sandbox",
58
+ },
59
+ };
60
+ default:
61
+ if (event.reason) {
62
+ Logger.warn("Unrecognized connection close reason", event.reason);
63
+ }
64
+ }
65
+
66
+ return {
67
+ kind: "retry",
68
+ status: { state: ConnectionState.CONNECTING },
69
+ };
70
+ }
@@ -108,8 +108,6 @@ describe("SseTransport", () => {
108
108
  transport.reconnect();
109
109
  await vi.waitFor(() => expect(opens).toHaveLength(1));
110
110
 
111
- // Stop retrying when the consumer treats the close as terminal,
112
- // mirroring useMarimoKernelConnection's terminal path.
113
111
  transport.addEventListener("close", () => transport.close());
114
112
  push('event: close\ndata: {"code": 1000, "reason": "MARIMO_SHUTDOWN"}\n\n');
115
113
  await vi.waitFor(() => expect(closes).toHaveLength(1));
@@ -146,8 +144,7 @@ describe("SseTransport", () => {
146
144
  fetchMock
147
145
  .mockResolvedValueOnce(sseResponse(first.stream))
148
146
  .mockResolvedValueOnce(sseResponse(second.stream));
149
- // Mirror useMarimoKernelConnection's retry path: reconnect()
150
- // synchronously from inside the close handler.
147
+ // Reconnect synchronously from the close handler.
151
148
  transport.addEventListener("close", () => transport.reconnect());
152
149
 
153
150
  transport.reconnect();
@@ -0,0 +1,31 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { createPyodideConnection } from "../../wasm/bridge";
4
+ import { isWasm } from "../../wasm/utils";
5
+ import { BasicTransport } from "./basic";
6
+ import { SseTransport } from "./sse";
7
+ import { WsTransport } from "./ws";
8
+ import type { IConnectionTransport } from "./transport";
9
+
10
+ export interface ConnectionTransportOptions {
11
+ url: () => string;
12
+ static: boolean;
13
+ transportType: "websocket" | "sse";
14
+ headers: () => Record<string, string>;
15
+ }
16
+
17
+ export function createConnectionTransport(
18
+ options: ConnectionTransportOptions,
19
+ ): IConnectionTransport {
20
+ if (options.static) {
21
+ return BasicTransport.empty();
22
+ }
23
+ if (isWasm()) {
24
+ return createPyodideConnection();
25
+ }
26
+ // Read the URL lazily to follow runtime redirects.
27
+ if (options.transportType === "sse") {
28
+ return new SseTransport(options.url, options.headers);
29
+ }
30
+ return new WsTransport(options.url);
31
+ }
@@ -1,30 +1,19 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
- export type ConnectionEvent = "open" | "close" | "message" | "error";
3
2
 
4
- export interface IConnectionTransportMap {
5
- open: WebSocketEventMap["open"];
6
- message: WebSocketEventMap["message"];
7
- close: WebSocketEventMap["close"];
8
- error: WebSocketEventMap["error"];
9
- }
10
-
11
- export type ConnectionTransportCallback<T extends ConnectionEvent> = (
12
- data: IConnectionTransportMap[T],
13
- ) => void;
3
+ import type {
4
+ NotebookTransport,
5
+ ConnectionEvent,
6
+ ConnectionTransportCallback,
7
+ IConnectionTransportMap,
8
+ } from "../../host/connection";
9
+ export type {
10
+ ConnectionEvent,
11
+ ConnectionTransportCallback,
12
+ IConnectionTransportMap,
13
+ } from "../../host/connection";
14
14
 
15
- export interface IConnectionTransport {
16
- reconnect(code?: number | undefined, reason?: string | undefined): void;
17
- close(): void;
15
+ export interface IConnectionTransport extends NotebookTransport {
18
16
  send(data: string | ArrayBuffer | Blob | ArrayBufferView<ArrayBuffer>): void;
19
- addEventListener<T extends ConnectionEvent>(
20
- event: T,
21
- callback: ConnectionTransportCallback<T>,
22
- ): void;
23
- removeEventListener<T extends ConnectionEvent>(
24
- event: T,
25
- callback: ConnectionTransportCallback<T>,
26
- ): void;
27
- readyState: WebSocket["readyState"];
28
17
  }
29
18
 
30
19
  export class ConnectionSubscriptions {
@@ -1,47 +1,17 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
+ import type { ConnectionStatus as HostConnectionStatus } from "../host/connection";
3
4
  import type { NotificationMessageData } from "../kernel/messages";
4
5
 
6
+ export {
7
+ ConnectionState,
8
+ ConnectionClosedReason as WebSocketClosedReason,
9
+ } from "../host/connection";
10
+
5
11
  export type ConnectionPhase =
6
12
  | NotificationMessageData<"startup-progress">["phase"]
7
13
  | "reconnecting";
8
14
 
9
- export const ConnectionState = {
10
- NOT_STARTED: "NOT_STARTED",
11
- CONNECTING: "CONNECTING",
12
- OPEN: "OPEN",
13
- CLOSING: "CLOSING",
14
- CLOSED: "CLOSED",
15
- } as const;
16
-
17
- export type ConnectionState =
18
- (typeof ConnectionState)[keyof typeof ConnectionState];
19
-
20
- export const WebSocketClosedReason = {
21
- KERNEL_DISCONNECTED: "KERNEL_DISCONNECTED",
22
- KERNEL_STARTUP_ERROR: "KERNEL_STARTUP_ERROR",
23
- } as const;
24
-
25
- export type WebSocketClosedReason =
26
- (typeof WebSocketClosedReason)[keyof typeof WebSocketClosedReason];
27
-
28
- export type ConnectionStatus =
29
- | {
30
- state: typeof ConnectionState.CLOSED;
31
- code: WebSocketClosedReason;
32
- /**
33
- * Human-readable reason for closing the connection.
34
- */
35
- reason: string;
36
- phase?: ConnectionPhase;
37
- }
38
- | {
39
- state: typeof ConnectionState.CONNECTING;
40
- phase?: ConnectionPhase;
41
- }
42
- | {
43
- state:
44
- | typeof ConnectionState.OPEN
45
- | typeof ConnectionState.CLOSING
46
- | typeof ConnectionState.NOT_STARTED;
47
- };
15
+ export type ConnectionStatus = HostConnectionStatus & {
16
+ phase?: ConnectionPhase;
17
+ };
@@ -0,0 +1,80 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { useStore } from "jotai";
4
+ import { useEffect, useMemo } from "react";
5
+ import useEvent from "react-use-event-hook";
6
+ import { jsonParseWithSpecialChar } from "@/utils/json/json-parser";
7
+ import type {
8
+ NotificationMessage,
9
+ NotificationPayload,
10
+ } from "../kernel/messages";
11
+ import {
12
+ createNotebookClient,
13
+ type NotebookClient,
14
+ type NotebookConnection,
15
+ } from "../host/notebook-client";
16
+ import { classifyCloseEvent } from "./connection-state";
17
+ import {
18
+ createConnectionTransport,
19
+ type ConnectionTransportOptions,
20
+ } from "./transports/create";
21
+
22
+ type Options = ConnectionTransportOptions & {
23
+ sessionId: string;
24
+ messageFormat: NotebookConnection["messageFormat"];
25
+ waitUntilReady: () => Promise<void>;
26
+ checkHealth: () => Promise<boolean>;
27
+ onNotification: (notification: NotificationPayload) => void;
28
+ onMessageError: NotebookConnection["onMessageError"];
29
+ };
30
+
31
+ export function useNotebookClient(options: Options): NotebookClient {
32
+ const store = useStore();
33
+ const { sessionId, static: isStatic, transportType, messageFormat } = options;
34
+ const controller = useMemo(() => createNotebookClient(store), [store]);
35
+ const url = useEvent(options.url);
36
+ const headers = useEvent(options.headers);
37
+ const waitUntilReady = useEvent(options.waitUntilReady);
38
+ const checkHealth = useEvent(options.checkHealth);
39
+ const onNotification = useEvent(options.onNotification);
40
+ const onMessageError = useEvent(options.onMessageError);
41
+
42
+ useEffect(() => {
43
+ const lifetime = new AbortController();
44
+ controller.connect({
45
+ createTransport: (url) =>
46
+ createConnectionTransport({
47
+ url,
48
+ headers,
49
+ static: isStatic,
50
+ transportType,
51
+ }),
52
+ url,
53
+ messageFormat,
54
+ signal: lifetime.signal,
55
+ waitUntilReady,
56
+ checkHealth,
57
+ onMessageError,
58
+ // The host validates the envelope, not the native notification contents.
59
+ onNotification: (notification) =>
60
+ onNotification({ data: notification as NotificationMessage }),
61
+ decodeJson: jsonParseWithSpecialChar,
62
+ classifyClose: classifyCloseEvent,
63
+ });
64
+ return () => lifetime.abort();
65
+ }, [
66
+ controller,
67
+ sessionId,
68
+ isStatic,
69
+ transportType,
70
+ messageFormat,
71
+ url,
72
+ headers,
73
+ waitUntilReady,
74
+ checkHealth,
75
+ onNotification,
76
+ onMessageError,
77
+ ]);
78
+
79
+ return controller.client;
80
+ }