@marimo-team/frontend 0.25.3-dev10 → 0.25.3-dev12

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 (123) hide show
  1. package/dist/assets/{CellStatus-nZpuDkm7.js → CellStatus-DVOHpdeJ.js} +1 -1
  2. package/dist/assets/{ConnectedDataExplorerComponent-t8fH_TNc.js → ConnectedDataExplorerComponent-TprnxZeJ.js} +1 -1
  3. package/dist/assets/{ErrorBoundary-B4n9EnHB.js → ErrorBoundary-CM13wrd8.js} +2 -2
  4. package/dist/assets/{JsonOutput-BxB0NL8w.js → JsonOutput-Cx1iyibB.js} +1 -1
  5. package/dist/assets/{LazyAnyLanguageCodeMirror-CKcCbG2G.js → LazyAnyLanguageCodeMirror-B4fCknq1.js} +2 -2
  6. package/dist/assets/{MarimoErrorOutput-BTy4MOzX.js → MarimoErrorOutput-s_5GOX8k.js} +1 -1
  7. package/dist/assets/{RenderHTML-9bMgbYmo.js → RenderHTML-DX6i99Op.js} +1 -1
  8. package/dist/assets/{RunButton-CFQUNN5c.js → RunButton-BKoh0zYG.js} +1 -1
  9. package/dist/assets/{add-cell-with-ai-f_MnLCwI.js → add-cell-with-ai-BpD_790A.js} +6 -6
  10. package/dist/assets/{add-connection-dialog-CoE_PhuD.js → add-connection-dialog-B1BXCjex.js} +2 -2
  11. package/dist/assets/{agent-panel-BWZz24jK.js → agent-panel-CDFUc1uu.js} +1 -1
  12. package/dist/assets/{ai-model-dropdown-zm-2fXzf.js → ai-model-dropdown-i1vfGeVi.js} +2 -2
  13. package/dist/assets/{any-language-editor-BLCAD3yx.js → any-language-editor-BdTWdMCI.js} +1 -1
  14. package/dist/assets/{app-config-button-BhU0ulYs.js → app-config-button-AdV-QDD7.js} +1 -1
  15. package/dist/assets/{blob-iOBwGaP9.js → blob-BG8yoQmx.js} +1 -1
  16. package/dist/assets/{cell-editor-CAj1UKxx.js → cell-editor-BKJ06H5F.js} +5 -5
  17. package/dist/assets/cell-link-8Auigi8P.js +1 -0
  18. package/dist/assets/{cells-rA_jsZCj.js → cells-C6YGYGdi.js} +56 -56
  19. package/dist/assets/{chat-display-BmEPmhDP.js → chat-display-DhgI2MS9.js} +1 -1
  20. package/dist/assets/{chat-panel-Ce2Xh7dM.js → chat-panel-Cau71Ygr.js} +1 -1
  21. package/dist/assets/{chat-ui-DTy98rP3.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-DU6dT80y.js → column-preview-BZ_iNhtJ.js} +1 -1
  25. package/dist/assets/{command-palette-BMqFKXVr.js → command-palette-DDC3kBet.js} +1 -1
  26. package/dist/assets/{common-lisZ0l6a.js → common-Sl3uthjg.js} +1 -1
  27. package/dist/assets/{components-Dvd1GnD6.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-DJLAC21v.js → config-DRIpOKqh.js} +1 -1
  32. package/dist/assets/config-Dy28fDwn.js +1 -0
  33. package/dist/assets/{datasource-Dr1xNKUn.js → datasource-DfbNb83e.js} +1 -1
  34. package/dist/assets/{dependency-graph-panel-B0uP-_id.js → dependency-graph-panel-L5cBE5sl.js} +1 -1
  35. package/dist/assets/{documentation-panel-DVl_ebWy.js → documentation-panel-D7S8JIc2.js} +1 -1
  36. package/dist/assets/{download-Bx55LMki.js → download-C03FOElq.js} +1 -1
  37. package/dist/assets/{edit-page-DlfCLUaq.js → edit-page-D_Rg0gQO.js} +6 -6
  38. package/dist/assets/{error-banner-Cd69vvyX.js → error-banner-COZ7SfYV.js} +1 -1
  39. package/dist/assets/{error-entries-DOWpdGvI.js → error-entries-CLnPDv7b.js} +1 -1
  40. package/dist/assets/{error-panel-DpbNKSLs.js → error-panel-D2xrYLue.js} +1 -1
  41. package/dist/assets/{errors-DCrsa1nH.js → errors-CNsgAw6m.js} +1 -1
  42. package/dist/assets/{feedback-button-B99ThNJW.js → feedback-button-DzPunIqU.js} +2 -2
  43. package/dist/assets/feedback-button-Kla7i5RT.js +1 -0
  44. package/dist/assets/{file-explorer-panel-Bo9eN3rW.js → file-explorer-panel-Uk4_QIJQ.js} +1 -1
  45. package/dist/assets/{file-name-input-ChnBsDE_.js → file-name-input-B4fRaQkI.js} +1 -1
  46. package/dist/assets/{floating-outline-MYqQW4Vp.js → floating-outline-B5MaM1JS.js} +1 -1
  47. package/dist/assets/{gallery-page-DSL0J4md.js → gallery-page-BefvdsM7.js} +1 -1
  48. package/dist/assets/{glide-data-editor-B1MWlKeM.js → glide-data-editor-C3eaYlXg.js} +1 -1
  49. package/dist/assets/{home-page-KrhQ55Vg.js → home-page-hmvKlc1w.js} +1 -1
  50. package/dist/assets/{hooks-BIogTGCr.js → hooks-DzML9nez.js} +1 -1
  51. package/dist/assets/index-ptK1gxmo.js +30 -0
  52. package/dist/assets/{layout-DOpdUwym.js → layout-Ck1RoL1B.js} +5 -5
  53. package/dist/assets/{logs-panel-y0XnfulJ.js → logs-panel-BRytaWaO.js} +1 -1
  54. package/dist/assets/{markdown-renderer-Q5AhbRU3.js → markdown-renderer-DX0Xrrfy.js} +1 -1
  55. package/dist/assets/{mermaid-a3xYymTy.js → mermaid-C4EiVqcd.js} +1 -1
  56. package/dist/assets/{name-cell-input-BNOnF6-0.js → name-cell-input-DlpTvPLa.js} +1 -1
  57. package/dist/assets/{outline-panel-Be7stWwv.js → outline-panel-B2WIpz-1.js} +1 -1
  58. package/dist/assets/{packages-panel-ClxOAEGy.js → packages-panel-CI5ff9b-.js} +1 -1
  59. package/dist/assets/{panel-accordion-state-GeMEdCPw.js → panel-accordion-state-BqSH5ttk.js} +1 -1
  60. package/dist/assets/panels-BCcZ_yLK.js +107 -0
  61. package/dist/assets/{process-output-DNFKMODr.js → process-output-f3C3PD0Z.js} +1 -1
  62. package/dist/assets/{readonly-code-display-YULOXf4h.js → readonly-code-display-MnMEoYW5.js} +1 -1
  63. package/dist/assets/{readonly-python-code-CadgXgRu.js → readonly-python-code-ggs4hAzC.js} +1 -1
  64. package/dist/assets/{renderShortcut-BkVJQ_Zh.js → renderShortcut-C1JwYcqW.js} +1 -1
  65. package/dist/assets/{reveal-component-BVAR7ayZ.js → reveal-component-PiV1eQk9.js} +1 -1
  66. package/dist/assets/run-page-AfZhtEh1.js +1 -0
  67. package/dist/assets/{runtime-details-state-DuChzsA6.js → runtime-details-state-CoL-p0MC.js} +1 -1
  68. package/dist/assets/{schemas-Dv42wLFq.js → schemas-ChRqMOTL.js} +1 -1
  69. package/dist/assets/{scratchpad-panel-By-ybhjg.js → scratchpad-panel-D2rWg4k8.js} +1 -1
  70. package/dist/assets/{secrets-panel-Bctbtkrp.js → secrets-panel-D7KN4tVs.js} +1 -1
  71. package/dist/assets/{session-panel-BmW6PH4z.js → session-panel-CnANhcTo.js} +1 -1
  72. package/dist/assets/{snippets-panel-CQ1dn3lA.js → snippets-panel-ClGHtEKQ.js} +1 -1
  73. package/dist/assets/{state-z3dlofZB.js → state-CbjBtkdd.js} +3 -3
  74. package/dist/assets/{state-Bfh_2SLl.js → state-DtGB7xeJ.js} +1 -1
  75. package/dist/assets/{terminal-Ck_vK-34.js → terminal-wnSHYT8N.js} +1 -1
  76. package/dist/assets/{tracing-BARlJ_Dk.js → tracing-Dz2rYOFL.js} +1 -1
  77. package/dist/assets/{tracing-panel-dgnr2fIN.js → tracing-panel-DqQY9V91.js} +2 -2
  78. package/dist/assets/{tree-actions-DAd80MbP.js → tree-actions-DnUokmva.js} +1 -1
  79. package/dist/assets/{useAddCell-DGstJK0S.js → useAddCell-IN24thff.js} +1 -1
  80. package/dist/assets/{useBoolean-BDD6_xQQ.js → useBoolean-d0Tty94N.js} +1 -1
  81. package/dist/assets/{useCellActionButton-DY9RlJTb.js → useCellActionButton-DmaFZ4oF.js} +1 -1
  82. package/dist/assets/{useDeleteCell-DFBGlSQk.js → useDeleteCell-fQlM46Y2.js} +1 -1
  83. package/dist/assets/{useDependencyPanelTab-DydxsE5d.js → useDependencyPanelTab-ImXYINm_.js} +1 -1
  84. package/dist/assets/{useInstallPackage-iX1eufdl.js → useInstallPackage-BW06eter.js} +1 -1
  85. package/dist/assets/{useNotebookActions-DfvB7RD_.js → useNotebookActions-DHt3s-ia.js} +1 -1
  86. package/dist/assets/{useRunCells-BFjFX3hz.js → useRunCells-DSJPXE-j.js} +1 -1
  87. package/dist/assets/{useSplitCell-Tqv5pPtL.js → useSplitCell-DQyMF5gd.js} +1 -1
  88. package/dist/assets/{useTheme-COhSHkCj.js → useTheme-n8Dj1zH7.js} +1 -1
  89. package/dist/assets/{vega-component-CnadTgoE.js → vega-component-IouMe13t.js} +1 -1
  90. package/dist/index.html +32 -32
  91. package/package.json +1 -1
  92. package/src/__tests__/mount.test.ts +20 -0
  93. package/src/core/edit-app.tsx +2 -2
  94. package/src/core/host/__tests__/events.test.ts +46 -0
  95. package/src/core/host/__tests__/notebook-client.test.ts +191 -0
  96. package/src/core/host/connection.ts +81 -0
  97. package/src/core/host/events.ts +60 -0
  98. package/src/core/host/notebook-client.ts +226 -0
  99. package/src/core/run-app.tsx +2 -2
  100. package/src/core/runtime/__tests__/runtime.test.ts +55 -11
  101. package/src/core/runtime/runtime.ts +14 -1
  102. package/src/core/runtime/types.ts +2 -0
  103. package/src/core/websocket/__tests__/{useMarimoKernelConnection.test.ts → connection-state.test.ts} +15 -17
  104. package/src/core/websocket/__tests__/{useWebSocket.test.ts → createConnectionTransport.test.ts} +1 -1
  105. package/src/core/websocket/__tests__/useNotebookClient.hook.test.tsx +140 -0
  106. package/src/core/websocket/__tests__/{useMarimoKernelConnection.hook.test.tsx → useNotebookSession.hook.test.tsx} +227 -133
  107. package/src/core/websocket/connection-state.ts +70 -0
  108. package/src/core/websocket/transports/__tests__/sse.test.ts +1 -4
  109. package/src/core/websocket/transports/create.ts +31 -0
  110. package/src/core/websocket/transports/transport.ts +12 -23
  111. package/src/core/websocket/types.ts +9 -39
  112. package/src/core/websocket/useNotebookClient.ts +80 -0
  113. package/src/core/websocket/{useMarimoKernelConnection.tsx → useNotebookSession.tsx} +64 -231
  114. package/src/mount.tsx +4 -0
  115. package/dist/assets/cell-link-1CynTVGQ.js +0 -1
  116. package/dist/assets/code-visibility-B-_GvwR5.js +0 -1
  117. package/dist/assets/coerce-CpE6kUdX.js +0 -1
  118. package/dist/assets/config-DX8Y2WbJ.js +0 -1
  119. package/dist/assets/feedback-button-DlqZkJwz.js +0 -1
  120. package/dist/assets/index-B3fcrmuf.js +0 -30
  121. package/dist/assets/panels-DfCmzLtT.js +0 -4
  122. package/dist/assets/run-page-Cxu1yV8Q.js +0 -1
  123. package/src/core/websocket/useWebSocket.tsx +0 -88
@@ -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
+ }
@@ -1,8 +1,8 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
3
  import { invalidatePackageData } from "@/core/packages/package-data";
4
- import { useAtom, useAtomValue, useSetAtom } from "jotai";
5
- import { useRef } from "react";
4
+ import { useAtom, useAtomValue, useSetAtom, useStore } from "jotai";
5
+ import { useLayoutEffect } from "react";
6
6
  import { useErrorBoundary } from "react-error-boundary";
7
7
  import { toast } from "@/components/ui/use-toast";
8
8
  import { attachmentsAtom } from "@/core/attachments/state";
@@ -13,8 +13,7 @@ import type {
13
13
  NotificationMessageData,
14
14
  NotificationPayload,
15
15
  } from "@/core/kernel/messages";
16
- import { TRANSPORT_EXHAUSTED_REASON } from "@/core/websocket/transports/ws";
17
- import { useConnectionTransport } from "@/core/websocket/useWebSocket";
16
+ import { useNotebookClient } from "@/core/websocket/useNotebookClient";
18
17
  import { renderHTML } from "@/plugins/core/RenderHTML";
19
18
  import {
20
19
  handleWidgetMessage,
@@ -22,11 +21,7 @@ import {
22
21
  } from "@/plugins/impl/anywidget/registry";
23
22
  import { logNever } from "@/utils/assertNever";
24
23
  import { prettyError } from "@/utils/errors";
25
- import {
26
- type JsonString,
27
- safeExtractSetUIElementMessageBuffers,
28
- } from "@/utils/json/base64";
29
- import { jsonParseWithSpecialChar } from "@/utils/json/json-parser";
24
+ import { safeExtractSetUIElementMessageBuffers } from "@/utils/json/base64";
30
25
  import { Logger } from "@/utils/Logger";
31
26
  import { reloadSafe } from "@/utils/reload-safe";
32
27
  import { useAlertActions } from "../alerts/state";
@@ -71,7 +66,7 @@ import { type LayoutState, useLayoutActions } from "../layout/state";
71
66
  import { kioskModeAtom } from "../mode";
72
67
  import { connectionAtom, startupProgressAtom } from "../network/connection";
73
68
  import type { RequestId } from "../network/DeferredRequestRegistry";
74
- import { useRuntimeManager } from "../runtime/config";
69
+ import { runtimeConfigAtom, useRuntimeManager } from "../runtime/config";
75
70
  import { SECRETS_REGISTRY } from "../secrets/request-registry";
76
71
  import { isStaticNotebook } from "../static/static-state";
77
72
  import {
@@ -82,104 +77,10 @@ import { useStorageActions } from "../storage/state";
82
77
  import { useVariablesActions } from "../variables/state";
83
78
  import type { VariableName } from "../variables/types";
84
79
  import { isWasm } from "../wasm/utils";
85
- import {
86
- type ConnectionStatus,
87
- WebSocketClosedReason,
88
- ConnectionState,
89
- } from "./types";
80
+ import { WebSocketClosedReason, ConnectionState } from "./types";
90
81
 
91
82
  const SUPPORTS_LAZY_KERNELS = true;
92
83
 
93
- // All MARIMO_* reasons except TRANSPORT_EXHAUSTED are emitted by the backend
94
- // (marimo/_server/api/endpoints/ws_endpoint.py and ws/*.py). Keep in sync with
95
- // the backend literals. Over the SSE transport these arrive as in-band
96
- // `close` events rather than WebSocket close frames, so every terminal
97
- // reason must be classified here — an unclassified reason falls into the
98
- // retry path.
99
- export type CloseReason =
100
- | "MARIMO_NO_FILE_KEY"
101
- | "MARIMO_NO_SESSION_ID"
102
- | "MARIMO_NO_SESSION"
103
- | "MARIMO_SHUTDOWN"
104
- | "MARIMO_KERNEL_STARTUP_ERROR"
105
- | "MARIMO_UNAUTHORIZED"
106
- | "MARIMO_KIOSK_NOT_ALLOWED"
107
- | typeof TRANSPORT_EXHAUSTED_REASON;
108
-
109
- export type CloseDecision =
110
- | { kind: "terminal"; status: ConnectionStatus; closeTransport: boolean }
111
- | { kind: "gave-up"; status: ConnectionStatus }
112
- | { kind: "retry"; status: ConnectionStatus };
113
-
114
- export function classifyCloseEvent(event: { reason?: string }): CloseDecision {
115
- switch (event.reason as CloseReason | undefined) {
116
- case TRANSPORT_EXHAUSTED_REASON:
117
- return {
118
- kind: "gave-up",
119
- status: {
120
- state: ConnectionState.CLOSED,
121
- code: WebSocketClosedReason.KERNEL_DISCONNECTED,
122
- reason: "kernel not found",
123
- },
124
- };
125
- case "MARIMO_NO_FILE_KEY":
126
- case "MARIMO_NO_SESSION_ID":
127
- case "MARIMO_NO_SESSION":
128
- case "MARIMO_SHUTDOWN":
129
- return {
130
- kind: "terminal",
131
- status: {
132
- state: ConnectionState.CLOSED,
133
- code: WebSocketClosedReason.KERNEL_DISCONNECTED,
134
- reason: "kernel not found",
135
- },
136
- closeTransport: true,
137
- };
138
- case "MARIMO_UNAUTHORIZED":
139
- return {
140
- kind: "terminal",
141
- status: {
142
- state: ConnectionState.CLOSED,
143
- code: WebSocketClosedReason.KERNEL_DISCONNECTED,
144
- reason: "not authorized",
145
- },
146
- closeTransport: true,
147
- };
148
- case "MARIMO_KIOSK_NOT_ALLOWED":
149
- return {
150
- kind: "terminal",
151
- status: {
152
- state: ConnectionState.CLOSED,
153
- code: WebSocketClosedReason.KERNEL_DISCONNECTED,
154
- reason: "kiosk mode is not available for this session",
155
- },
156
- closeTransport: true,
157
- };
158
- case "MARIMO_KERNEL_STARTUP_ERROR":
159
- return {
160
- kind: "terminal",
161
- status: {
162
- state: ConnectionState.CLOSED,
163
- code: WebSocketClosedReason.KERNEL_STARTUP_ERROR,
164
- reason: "Failed to start kernel sandbox",
165
- },
166
- closeTransport: true,
167
- };
168
- default:
169
- // Empty/undefined reasons are normal transient closes. Anything else is
170
- // an unknown server reason; warn so a new MARIMO_* reason doesn't fall
171
- // silently into the retry path.
172
- if (event.reason) {
173
- logNever(event.reason as never);
174
- }
175
- }
176
-
177
- return {
178
- kind: "retry",
179
- status: { state: ConnectionState.CONNECTING },
180
- };
181
- }
182
-
183
84
  function getExistingCells(): CellData[] | undefined {
184
85
  if (!SUPPORTS_LAZY_KERNELS) {
185
86
  return undefined;
@@ -191,16 +92,12 @@ function getExistingCells(): CellData[] | undefined {
191
92
  );
192
93
  }
193
94
 
194
- /**
195
- * Creates a connection to the Marimo kernel and handles incoming messages.
196
- */
197
- export function useMarimoKernelConnection(opts: {
95
+ /** Coordinate session startup and apply notifications to the editor. */
96
+ export function useNotebookSession(opts: {
198
97
  sessionId: SessionId;
199
98
  autoInstantiate: boolean;
200
99
  setCells: (cells: CellData[], layout: LayoutState) => void;
201
100
  }) {
202
- // Track whether we want to try reconnecting.
203
- const shouldTryReconnecting = useRef<boolean>(true);
204
101
  const { autoInstantiate, sessionId, setCells } = opts;
205
102
  const { showBoundary } = useErrorBoundary();
206
103
 
@@ -237,26 +134,26 @@ export function useMarimoKernelConnection(opts: {
237
134
  } = useAlertActions();
238
135
  const setKioskMode = useSetAtom(kioskModeAtom);
239
136
  const setStableSessionId = useSetAtom(stableSessionIdAtom);
240
- const setAttachments = useSetAtom(attachmentsAtom);
241
137
  const setCapabilities = useSetAtom(capabilitiesAtom);
242
138
  const runtimeManager = useRuntimeManager();
139
+ const { notebookId } = useAtomValue(runtimeConfigAtom);
243
140
  const transportType = useAtomValue(connectionTransportTypeAtom);
244
141
  const setCacheInfo = useSetAtom(cacheInfoAtom);
245
142
  const setKernelStartupError = useSetAtom(kernelStartupErrorAtom);
143
+ const setAttachments = useSetAtom(attachmentsAtom);
246
144
  const {
247
145
  setNamespaces: setStorageNamespaces,
248
146
  filterFromVariables: filterStorageFromVariables,
249
147
  } = useStorageActions();
250
148
 
251
- const handleMessage = (e: MessageEvent<JsonString<NotificationPayload>>) => {
252
- const msg = jsonParseWithSpecialChar(e.data);
149
+ const handleNotification = (msg: NotificationPayload) => {
253
150
  switch (msg.data.op) {
254
- case "reload":
255
- reloadSafe();
256
- return;
257
151
  case "attachments":
258
152
  setAttachments(msg.data.attachments);
259
153
  return;
154
+ case "reload":
155
+ reloadSafe();
156
+ return;
260
157
  case "startup-progress": {
261
158
  const { phase } = msg.data;
262
159
  updateStartupProgress(msg.data);
@@ -511,109 +408,59 @@ export function useMarimoKernelConnection(opts: {
511
408
  }
512
409
  };
513
410
 
514
- const tryReconnecting = (code?: number, reason?: string) => {
515
- // If not properly gated, we could try reconnecting forever if the
516
- // issue is not transient. So we want to try reconnecting only once after an
517
- // open connection is closed.
518
- if (shouldTryReconnecting.current) {
519
- shouldTryReconnecting.current = false;
520
- ws.reconnect(code, reason);
521
- }
411
+ const store = useStore();
412
+ const onMessageError = (error: unknown) => {
413
+ toast({
414
+ title: "Failed to handle message",
415
+ description: prettyError(error),
416
+ variant: "danger",
417
+ });
522
418
  };
523
-
524
- // Manual reconnect. Probes /health first to fail fast when the runtime
525
- // is unreachable, instead of waiting on partysocket's retry budget.
526
- const reconnect = async () => {
527
- if (
528
- ws.readyState === WebSocket.OPEN ||
529
- ws.readyState === WebSocket.CONNECTING
530
- ) {
531
- return;
532
- }
533
- shouldTryReconnecting.current = true;
534
- setKernelStartupError(null);
535
- setConnection({ state: ConnectionState.CONNECTING });
536
- const healthy = await runtimeManager.reconcileFromHealth();
537
- if (!healthy) {
538
- shouldTryReconnecting.current = false;
539
- setConnection({
540
- state: ConnectionState.CLOSED,
541
- code: WebSocketClosedReason.KERNEL_DISCONNECTED,
542
- reason: "kernel not found",
543
- });
544
- return;
545
- }
546
- ws.reconnect();
547
- };
548
-
549
- const ws = useConnectionTransport({
419
+ const notebook = useNotebookClient({
420
+ sessionId,
550
421
  static: isStaticNotebook(),
551
422
  transportType,
552
- /**
553
- * Unique URL for this session.
554
- */
423
+ messageFormat:
424
+ notebookId && transportType === "websocket" ? "host" : "session",
555
425
  url: () =>
556
426
  transportType === "sse"
557
427
  ? runtimeManager.getSseURL(sessionId).toString()
558
428
  : runtimeManager.getWsURL(sessionId).toString(),
559
- /**
560
- * Auth headers for the SSE transport. WebSockets cannot send headers
561
- * and instead put the access token in the URL.
562
- */
429
+ // SSE uses headers for auth. WebSockets carry the token in the URL.
563
430
  headers: () => runtimeManager.headers(),
564
-
565
- /**
566
- * The transport is open; kernel-ready establishes session readiness.
567
- */
568
- onOpen: async () => {
569
- updateStartupProgress(null);
570
- setAttachments([]);
571
- // If we are open, we can reset our reconnecting flag.
572
- shouldTryReconnecting.current = true;
573
- },
574
-
575
- /**
576
- * Wait to connect, in case the remote kernel still starting up.
577
- */
578
- waitToConnect: async () => {
579
- if (isStaticNotebook()) {
580
- return;
581
- }
582
- if (isWasm()) {
583
- return;
584
- }
585
-
586
- if (runtimeManager.isSameOrigin) {
587
- return;
431
+ waitUntilReady: async () => {
432
+ if (!isStaticNotebook() && !isWasm() && !runtimeManager.isSameOrigin) {
433
+ await runtimeManager.waitForHealthy();
588
434
  }
589
- await runtimeManager.waitForHealthy();
590
435
  },
591
-
592
- /**
593
- * Handle messages sent by the kernel.
594
- */
595
- onMessage: (e) => {
436
+ checkHealth: () => runtimeManager.reconcileFromHealth(),
437
+ onMessageError,
438
+ onNotification: (notification) => {
596
439
  try {
597
- handleMessage(e);
440
+ handleNotification(notification);
598
441
  } catch (error) {
599
- Logger.error("Failed to handle message", e.data, error);
600
- toast({
601
- title: "Failed to handle message",
602
- description: prettyError(error),
603
- variant: "danger",
604
- });
442
+ Logger.error("Failed to handle notification", notification, error);
443
+ onMessageError(error);
605
444
  }
606
445
  },
446
+ });
607
447
 
608
- /**
609
- * Handle a close event. We may want to reconnect.
610
- */
611
- onClose: (e) => {
612
- Logger.warn("WebSocket closed", e.code, e.reason);
448
+ // Subscribe before passive connection setup: static/WASM transports can emit synchronously.
449
+ useLayoutEffect(() => {
450
+ const updateConnection = () => {
451
+ const status = store.get(notebook.connectionAtom);
452
+ if (status.state === ConnectionState.NOT_STARTED) {
453
+ return;
454
+ }
613
455
  setAttachments([]);
614
- const decision = classifyCloseEvent(e);
456
+ if (status.state === ConnectionState.OPEN) {
457
+ updateStartupProgress(null);
458
+ return; // kernel-ready, not the socket, establishes session readiness.
459
+ }
460
+ if (status.state === ConnectionState.CONNECTING) {
461
+ setKernelStartupError(null);
462
+ }
615
463
  setConnection((previous) => {
616
- const status = decision.status;
617
464
  if (
618
465
  status.state === ConnectionState.CLOSED &&
619
466
  status.code === WebSocketClosedReason.KERNEL_STARTUP_ERROR &&
@@ -632,32 +479,18 @@ export function useMarimoKernelConnection(opts: {
632
479
  }
633
480
  return status;
634
481
  });
635
- if (decision.kind === "terminal" && decision.closeTransport) {
636
- ws.close(); // close to prevent reconnecting
637
- return;
638
- }
639
- if (decision.kind === "retry") {
640
- // Session should be valid
641
- // - browser tab might have been closed or re-opened
642
- // - computer might have just woken from sleep
643
- tryReconnecting(e.code, e.reason);
644
- }
645
- },
646
-
647
- /**
648
- * When we encounter an error, we should close the connection.
649
- */
650
- onError: (e) => {
651
- Logger.warn("WebSocket error", e);
652
- setAttachments([]);
653
- setConnection({
654
- state: ConnectionState.CLOSED,
655
- code: WebSocketClosedReason.KERNEL_DISCONNECTED,
656
- reason: "kernel not found",
657
- });
658
- tryReconnecting();
659
- },
660
- });
661
-
662
- return { connection, reconnect };
482
+ };
483
+ const unsubscribe = store.sub(notebook.connectionAtom, updateConnection);
484
+ updateConnection();
485
+ return unsubscribe;
486
+ }, [
487
+ notebook,
488
+ store,
489
+ setConnection,
490
+ setAttachments,
491
+ setKernelStartupError,
492
+ updateStartupProgress,
493
+ ]);
494
+
495
+ return { notebook, connection, reconnect: notebook.reconnect };
663
496
  }
package/src/mount.tsx CHANGED
@@ -274,6 +274,8 @@ const mountOptionsSchema = z.object({
274
274
  .transform((val) => val as api.Notebook["NotebookV1"]),
275
275
  ]),
276
276
 
277
+ notebookId: z.string().optional(),
278
+
277
279
  /**
278
280
  * Runtime configs
279
281
  */
@@ -284,6 +286,7 @@ const mountOptionsSchema = z.object({
284
286
  // Lazy by default, but can be overridden by the runtime config
285
287
  lazy: z.boolean().default(true),
286
288
  authToken: z.string().nullish(),
289
+ notebookId: z.string().optional(),
287
290
  }),
288
291
  )
289
292
  .nullish()
@@ -364,6 +367,7 @@ function initStore(options: unknown) {
364
367
  } else {
365
368
  store.set(runtimeConfigAtom, {
366
369
  ...DEFAULT_RUNTIME_CONFIG,
370
+ notebookId: parsedOptions.data.notebookId,
367
371
  lazy: false,
368
372
  serverToken: parsedOptions.data.serverToken,
369
373
  });
@@ -1 +0,0 @@
1
- import{c as e,l as t,t as n,u as r}from"./useEvent-Dfcbgjhg.js";import{D as i,In as a,M as o,P as s,gr as c,xo as l,za as u}from"./cells-rA_jsZCj.js";import{t as d}from"./compiler-runtime-BrajqUja.js";import{p as f}from"./config-VPSq73n8.js";import{g as p}from"./useEventListener-D9cftcHj.js";import{i as m}from"./utils-B4K466FK.js";import{c as h}from"./session-DqoWmb7X.js";import{_ as g,w as _}from"./config-DX8Y2WbJ.js";import{t as v}from"./cn-ldLVeaqm.js";import{t as y}from"./jsx-runtime-B74pBk57.js";import{n as b}from"./paths-bkIFVj8h.js";import{b as x}from"./write-secret-modal-DzKAInL2.js";import{r as S}from"./requests-lt0JK8Jt.js";var C=d();function w(){return t(u)}function T(){let t=(0,C.c)(5),[i]=e(g),a=r(u),{openAlert:o}=x(),{sendRename:s}=S(),c;return t[0]!==i.state||t[1]!==o||t[2]!==s||t[3]!==a?(c=async e=>{let t=f();return i.state===_.OPEN?(h(t=>{e===null?t.delete(m.filePath):t.set(m.filePath,e)}),s({filename:e}).then(()=>(a(e),document.title=t.app_title||b.basename(e)||`Untitled Notebook`,e)).catch(e=>(o(e.message),null))):(o(`Failed to save notebook: not connected to a kernel.`),null)},t[0]=i.state,t[1]=o,t[2]=s,t[3]=a,t[4]=c):c=t[4],n(c)}var E=y(),D=e=>{let t=(0,C.c)(12),{className:n,cellId:r,variant:c,onClick:l,formatCellName:u,skipScroll:d}=e,f=s()[r]??``,p=o().inOrderIds.indexOf(r),{showCellIfHidden:m}=i(),h=u??j,g;t[0]===n?g=t[1]:(g=v(`inline-block cursor-pointer text-link hover:underline`,n),t[0]=n,t[1]=g);let _;t[2]!==r||t[3]!==l||t[4]!==m||t[5]!==d||t[6]!==c?(_=e=>{if(r===`__scratch__`)return!1;m({cellId:r}),e.stopPropagation(),e.preventDefault(),requestAnimationFrame(()=>{A(r,c,d)&&l?.()})},t[2]=r,t[3]=l,t[4]=m,t[5]=d,t[6]=c,t[7]=_):_=t[7];let y=h(a(f,p)),b;return t[8]!==g||t[9]!==_||t[10]!==y?(b=(0,E.jsx)(`div`,{className:g,role:`link`,tabIndex:-1,onClick:_,children:y}),t[8]=g,t[9]=_,t[10]=y,t[11]=b):b=t[11],b},O=e=>{let t=(0,C.c)(2),n;return t[0]===e?n=t[1]:(n=(0,E.jsx)(D,{...e,variant:`destructive`}),t[0]=e,t[1]=n),n},k=e=>{let t=(0,C.c)(10),{cellId:n,lineNumber:r}=e,i=w(),a;t[0]!==n||t[1]!==r?(a=()=>c(n,r),t[0]=n,t[1]=r,t[2]=a):a=t[2];let o;t[3]!==n||t[4]!==i?(o=e=>n===`__scratch__`?`scratch`:`marimo://${i||`untitled`}#cell=${e}`,t[3]=n,t[4]=i,t[5]=o):o=t[5];let s;return t[6]!==n||t[7]!==a||t[8]!==o?(s=(0,E.jsx)(D,{cellId:n,onClick:a,skipScroll:!0,variant:`destructive`,className:`traceback-cell-link`,formatCellName:o}),t[6]=n,t[7]=a,t[8]=o,t[9]=s):s=t[9],s};function A(e,t,n){let r=l.create(e),i=document.getElementById(r),a=i?.classList.contains(`has-error`);return i===null?(p.error(`Cell ${r} not found on page.`),!1):(n||i.scrollIntoView({behavior:`smooth`,block:`center`}),(t===`destructive`||a&&t===void 0)&&(i.classList.add(`error-outline`),setTimeout(()=>{i.classList.remove(`error-outline`)},2e3)),(t===`focus`||!a&&t===void 0)&&(i.classList.add(`focus-outline`),setTimeout(()=>{i.classList.remove(`focus-outline`)},2e3)),!0)}function j(e){return e}export{w as a,A as i,O as n,T as o,k as r,D as t};
@@ -1 +0,0 @@
1
- import{f as e,l as t}from"./useEvent-Dfcbgjhg.js";import{t as n}from"./compiler-runtime-BrajqUja.js";import{i as r,t as i}from"./utils-B4K466FK.js";import{m as a,v as o}from"./session-DqoWmb7X.js";import{M as s,_ as c,l,w as u}from"./config-DX8Y2WbJ.js";import{r as d}from"./state-BAJYRhmO.js";import{i as f,n as p,o as m}from"./state-C3oDoyFe.js";import{i as h}from"./strings-B2vv-ENv.js";var g=n(),_={canHealthCheck:!1,canShutdown:!1,canRestart:!1,supportsLsp:!1},v={kind:`wasm`,label:`Pyodide`,capabilities:_,state:e(e=>{let t=e(m);switch(t.kind){case`loading`:return{kind:`connecting`,progress:{label:t.message}};case`ready`:return{kind:`ready`};case`error`:return{kind:`failed`,error:{message:t.message,errorKind:`init`}}}})},y={kind:`remote`,label:`Kernel`,capabilities:{canHealthCheck:!0,canShutdown:!0,canRestart:!0,supportsLsp:!0},state:e(e=>{let t=e(c);switch(t.state){case u.OPEN:return{kind:`ready`};case u.CONNECTING:return{kind:`connecting`,progress:{label:`Connecting…`}};case u.NOT_STARTED:return{kind:`connecting`,progress:{label:`Not connected`}};case u.CLOSING:case u.CLOSED:return{kind:`failed`,error:{message:t.state===u.CLOSED?t.reason:`Disconnecting`,errorKind:`runtime`}}}})},b={kind:`static`,label:`Static`,capabilities:_,state:e({kind:`ready`})},x={kind:`islands`,label:`Islands`,capabilities:_,state:e(e=>{let t=e(p);return t===!0?{kind:`ready`}:typeof t==`string`?{kind:`failed`,error:{message:t,errorKind:`init`}}:{kind:`connecting`,progress:{label:`Initializing islands…`}}})};function S(){return l()?b:i()?x:s()?v:y}var C=e(S());e(e=>{let t=e(C);return e(t.state).kind!==`ready`||t.kind===`static`?!1:t.kind!==`remote`||e(f).isInstantiated});function w(e){let n=(0,g.c)(2),i=t(a),{mode:s}=t(o),c=t(d),l=t(C).kind;if(i&&l===`remote`)return!0;switch(s){case`edit`:case`present`:return!0;case`home`:case`gallery`:return!1;case`read`:{if(!c||new URLSearchParams(window.location.search).get(r.includeCode)===`false`)return!1;let t;return n[0]===e?t=n[1]:(t=e.some(T),n[0]=e,n[1]=t),t}default:return h(s),!1}}function T(e){return!!e.code}export{C as n,w as t};
@@ -1 +0,0 @@
1
- import{H as e,U as t,V as n,i as r,l as i,p as a}from"./schemas-Dv42wLFq.js";function o(e){return t(a,e)}function s(t){return e(i,t)}function c(e){return n(r,e)}export{s as n,o as r,c as t};
@@ -1 +0,0 @@
1
- import{a as e,c as t,f as n,l as r,r as i,t as a}from"./useEvent-Dfcbgjhg.js";import{t as o}from"./compiler-runtime-BrajqUja.js";import{g as s}from"./useEventListener-D9cftcHj.js";import{t as c}from"./invariant-Ci2dTZ72.js";import{i as l,t as u}from"./utils-B4K466FK.js";import{p as d,t as f}from"./session-DqoWmb7X.js";import{t as p}from"./Deferred-CrTnjj37.js";var m=o();function h(){return typeof document<`u`&&document.querySelector(`marimo-wasm`)!==null}var ee=Symbol(``);function g(e,t){return n(e,function(e,n,r){n(this,t(e(this),r))})}function te(e,t){let n=null,r=new Map,i=new Set,a=i=>{let s;if(t===void 0)s=r.get(i);else for(let[e,n]of r)if(t(e,i)){s=n;break}if(s!==void 0){if(n?.(s[1],i))a.remove(i);else return s[0]}let c=e(i);return r.set(i,[c,Date.now()]),o(`CREATE`,i,c),c},o=(e,t,n)=>{for(let r of i)r({type:e,param:t,atom:n})};return a.unstable_listen=e=>(i.add(e),()=>{i.delete(e)}),a.getParams=()=>r.keys(),a.remove=e=>{if(t===void 0){if(!r.has(e))return;let[t]=r.get(e);r.delete(e),o(`REMOVE`,e,t)}else for(let[n,[i]]of r)if(t(n,e)){r.delete(n),o(`REMOVE`,n,i);break}},a.setShouldRemove=e=>{if(n=e,n)for(let[e,[t,i]]of r)n(i,e)&&(r.delete(e),o(`REMOVE`,e,t))},a}var _=(e,t,n)=>(t.has(n)?t:t.set(n,e())).get(n),ne=new WeakMap,v=(e,t,n,r)=>_(e,_(()=>new WeakMap,_(()=>new WeakMap,ne,t),n),r);function y(e,t,r=Object.is){return v(()=>{let i=Symbol(),a=([e,n])=>{if(n===i)return t(e);let a=t(e,n);return r(n,a)?n:a},o=n(t=>{let n=t(o),r=t(e);return a([r,n])});return o.init=i,o},e,t,r)}var b=(e,t,n)=>(t.has(n)?t:t.set(n,e())).get(n),x=new WeakMap,S=(e,t,n)=>b(e,b(()=>new WeakMap,x,t),n),C={},w=e=>!!e.write,re=e=>typeof e==`function`;function ie(e,t){return S(()=>{let r=new WeakMap,i=(o,s)=>{let c=r.get(o);if(c)return c;let l=s&&r.get(s),u=[],d=[];return o.forEach((r,s)=>{let c=t?t(r):s;d[s]=c;let f=l&&l.atomList[l.keyList.indexOf(c)];if(f){u[s]=f;return}let p=t=>{let n=t(a),r=t(e),s=i(r,n?.arr).keyList.indexOf(c);if(s<0||s>=r.length){let e=o[i(o).keyList.indexOf(c)];if(e)return e;throw Error(`splitAtom: index out of bounds for read`)}return r[s]};u[s]=w(e)?n(p,(t,n,r)=>{let o=t(a),s=t(e),l=i(s,o?.arr).keyList.indexOf(c);if(l<0||l>=s.length)throw Error(`splitAtom: index out of bounds for write`);let u=re(r)?r(s[l]):r;Object.is(s[l],u)||n(e,[...s.slice(0,l),u,...s.slice(l+1)])}):n(p)}),c=l&&l.keyList.length===d.length&&l.keyList.every((e,t)=>e===d[t])?l:{arr:o,atomList:u,keyList:d},r.set(o,c),c},a=n(t=>{let n=t(a),r=t(e);return i(r,n?.arr)});a.init=void 0;let o=w(e)?n(e=>e(a).atomList,(t,n,r)=>{switch(r.type){case`remove`:{let i=t(o).indexOf(r.atom);if(i>=0){let r=t(e);n(e,[...r.slice(0,i),...r.slice(i+1)])}break}case`insert`:{let i=r.before?t(o).indexOf(r.before):t(o).length;if(i>=0){let a=t(e);n(e,[...a.slice(0,i),r.value,...a.slice(i)])}break}case`move`:{let i=t(o).indexOf(r.atom),a=r.before?t(o).indexOf(r.before):t(o).length;if(i>=0&&a>=0){let r=t(e);i<a?n(e,[...r.slice(0,i),...r.slice(i+1,a),r[i],...r.slice(a)]):n(e,[...r.slice(0,a),r[i],...r.slice(a,i),...r.slice(i+1)])}break}}}):n(e=>e(a).atomList);return o},e,t||C)}var T=e=>typeof e?.then==`function`;function E(e=()=>{try{return window.localStorage}catch{return}},t){let n,r,i={getItem:(i,a)=>{let o=e=>{if(e||=``,n!==e){try{r=JSON.parse(e,t?.reviver)}catch{return a}n=e}return r},s=e()?.getItem(i)??null;return T(s)?s.then(o):o(s)},setItem:(n,r)=>e()?.setItem(n,JSON.stringify(r,t?.replacer)),removeItem:t=>e()?.removeItem(t)},a=e=>(n,r,i)=>e(n,e=>{let n;try{n=JSON.parse(e||``,t?.reviver)}catch{n=i}r(n)}),o;try{o=e()?.subscribe}catch{}return!o&&typeof window<`u`&&typeof window.addEventListener==`function`&&window.Storage&&(o=(t,n)=>{if(!(e()instanceof window.Storage))return()=>{};let r=r=>{r.storageArea===e()&&r.key===t&&n(r.newValue)};return window.addEventListener(`storage`,r),()=>{window.removeEventListener(`storage`,r)}}),o&&(i.subscribe=a(o)),i}var ae=E();function D(e,t,r=ae,i){let a=i?.getOnInit,o=n(a?r.getItem(e,t):t);return o.onMount=n=>(n(r.getItem(e,t)),r.subscribe?.call(r,e,n,t)),n(e=>e(o),(n,i,a)=>{let s=typeof a==`function`?a(n(o)):a;return s===ee?(i(o,t),r.removeItem(e)):T(s)?s.then(t=>(i(o,t),r.setItem(e,t))):(i(o,s),r.setItem(e,s))})}var O={NOT_STARTED:`NOT_STARTED`,CONNECTING:`CONNECTING`,OPEN:`OPEN`,CLOSING:`CLOSING`,CLOSED:`CLOSED`},k={KERNEL_DISCONNECTED:`KERNEL_DISCONNECTED`,KERNEL_STARTUP_ERROR:`KERNEL_STARTUP_ERROR`},A=n({state:O.NOT_STARTED}),j=g(null,(e,t)=>t===null?null:{phase:t.phase,logs:t.log_mode===`append`&&e?.phase===t.phase?e.logs+t.logs:t.logs});function M(){return e(A,e=>e.state===O.OPEN)}function N(){return d()?M():Promise.resolve()}var P=n(e=>e(A).state===O.CONNECTING),F=n(e=>e(A).state===O.OPEN),I=n(e=>{let t=e(A);return t.state===O.OPEN||t.state===O.NOT_STARTED});n(e=>e(A).state===O.CLOSED);var L=n(e=>e(A).state===O.NOT_STARTED);function R(e){return e===O.CONNECTING}function z(e){return e===O.OPEN}function B(e){return e===O.CLOSING}function V(e){return e===O.NOT_STARTED}function H(e){return e===O.CLOSED||e===O.CLOSING||e===O.CONNECTING}function oe(e){switch(e){case O.CLOSED:return`App disconnected`;case O.CONNECTING:return`Connecting to a runtime ...`;case O.CLOSING:return`App disconnecting...`;case O.OPEN:return``;case O.NOT_STARTED:return`Click to connect to a runtime`;default:return``}}function U(e){if(typeof e!=`object`||!e||Array.isArray(e))return!1;for(let t of Object.values(e))if(typeof t!=`string`)return!1;return!0}function W(e){if(typeof e!=`object`||!e||Array.isArray(e))return!1;let t=e;return!(!U(t.files)||t.modelNotifications!==void 0&&!Array.isArray(t.modelNotifications))}function G(){let e=typeof window>`u`?void 0:window.__MARIMO_STATIC__;return W(e)?e:void 0}function K(){return G()!==void 0}function q(){let e=G();return c(e!==void 0,`Not a static notebook`),e.files}function se(){return G()?.modelNotifications}var ce=class{initialHealthyCheck=new p;config;lazy;constructor(e,t=!1){this.config=e,this.lazy=t;try{new URL(this.config.url)}catch(e){throw Error(`Invalid runtime URL: ${this.config.url}. ${e instanceof Error?e.message:`Unknown error`}`,{cause:e})}this.lazy||this.init()}get isLazy(){return this.lazy}get httpURL(){return new URL(this.config.url)}get isSameOrigin(){return this.httpURL.origin===window.location.origin}get isServerless(){return h()||u()||K()}formatHttpURL({path:e=``,searchParams:t,restrictToKnownQueryParams:n=!0}){let r=this.httpURL,i=new URLSearchParams(window.location.search);if(t)for(let[e,n]of t.entries())r.searchParams.set(e,n);for(let[e,t]of i.entries())n&&!Object.values(l).includes(e)||r.searchParams.set(e,t);return r.pathname=`${r.pathname.replace(/\/$/,``)}/${e.replace(/^\//,``)}`,r.hash=``,r}appendCrossOriginAuth(e){return!this.isSameOrigin&&this.config.authToken&&e.searchParams.set(l.accessToken,this.config.authToken),e}formatNavigableHttpURL(e,t){let n=this.formatHttpURL({path:e,searchParams:t});return n.searchParams.delete(l.accessToken),this.appendCrossOriginAuth(n)}formatWsURL(e,t){let n=this.formatHttpURL({path:e,searchParams:t,restrictToKnownQueryParams:!1});return le(this.appendCrossOriginAuth(n).toString())}getSessionSearchParams(e){let t=new URL(this.config.url),n=new URLSearchParams(t.search);return new URLSearchParams(window.location.search).forEach((e,t)=>{n.has(t)||n.set(t,e)}),n.set(l.sessionId,e),n}getWsURL(e){return this.formatWsURL(`/ws`,this.getSessionSearchParams(e))}getSseURL(e){let t=this.formatHttpURL({path:`/sse`,searchParams:this.getSessionSearchParams(e),restrictToKnownQueryParams:!1});return t.searchParams.delete(l.accessToken),t}getWsSyncURL(e){return this.formatWsURL(`/ws_sync`,this.getSessionSearchParams(e))}getTerminalWsURL(e){let t=this.formatWsURL(`/terminal/ws`);return e&&(t.searchParams.set(`rows`,String(e.rows)),t.searchParams.set(`cols`,String(e.cols))),t}getLSPURL(e){if(e===`copilot`){let t=this.formatWsURL(`/lsp/${e}`),n=t.searchParams.get(l.accessToken);return t.search=``,n&&t.searchParams.set(l.accessToken,n),t}return this.formatWsURL(`/lsp/${e}`)}getAiURL(e){return this.formatHttpURL({path:`/api/ai/${e}`})}healthURL(){return this.formatHttpURL({path:`/health`})}async fetchHealth(){try{return await fetch(this.healthURL().toString())}catch(e){return s.error(`Failed to check health: ${e instanceof Error?e.message:`Unknown error`}`,{cause:e}),null}}async reconcileFromHealth(){if(this.isServerless)return!0;let e=await this.fetchHealth();if(!e)return!1;if(e.redirected){s.debug(`Runtime redirected to ${e.url}`);let t=new URL(e.url);t.pathname=t.pathname.replace(/\/health$/,``),this.config.url=t.toString()}return e.ok&&this.setDOMBaseUri(this.config.url),e.ok}async probeHealth(){return this.isServerless?!0:(await this.fetchHealth())?.ok??!1}setDOMBaseUri(e){e=e.split(`?`,1)[0],e.endsWith(`/`)||(e+=`/`);let t=document.querySelector(`base`);t?t.setAttribute(`href`,e):(t=document.createElement(`base`),t.setAttribute(`href`,e),document.head.append(t))}async init(e){s.debug(`Initializing runtime...`);let t=0;for(;!await this.reconcileFromHealth();){if(t>=25){s.error(`Failed to connect after 25 retries`),this.initialHealthyCheck.reject(Error(`Failed to connect after 25 retries`));return}if(!e?.disableRetryDelay){let e=Math.min(100*1.2**t,2e3);await new Promise(t=>setTimeout(t,e))}t++}s.debug(`Runtime is healthy`),this.initialHealthyCheck.resolve()}async waitForHealthy(){return this.initialHealthyCheck.promise}headers(){let e={"Marimo-Session-Id":f(),"Marimo-Server-Token":this.config.serverToken??``,"x-runtime-url":this.httpURL.toString()};return this.config.authToken&&(e.Authorization=`Bearer ${this.config.authToken}`),e}sessionHeaders(){return{"Marimo-Session-Id":f()}}};function le(e){if(!e.startsWith(`http`)){s.warn(`URL must start with http: ${e}`);let t=new URL(e);return t.protocol=`ws`,t}return new URL(e.replace(/^http/,`ws`))}function ue(){let e=new URL(document.baseURI);return e.search=``,e.hash=``,e.toString()}var J={lazy:!0,url:ue()},Y=n(J),X=n(e=>{let t=e(Y);return new ce(t,t.lazy)});function Z(){return r(X)}function Q(){let e=(0,m.c)(4),n=Z(),[r,i]=t(A),o;return e[0]!==r.state||e[1]!==n||e[2]!==i?(o=async()=>{V(r.state)?(i({state:O.CONNECTING}),await n.init()):s.log(`Runtime already started or starting...`)},e[0]=r.state,e[1]=n,e[2]=i,e[3]=o):o=e[3],a(o)}function $(){return i.get(X)}function de(e){if(e.startsWith(`http`))return new URL(e);let t=$().httpURL.toString();return t.startsWith(`blob:`)&&(t=t.replace(`blob:`,``)),new URL(e,t)}export{y as A,N as C,g as D,te as E,h as M,D as O,M as S,k as T,A as _,Q as a,L as b,q as c,B as d,z as f,I as g,V as h,Y as i,ie as j,E as k,K as l,H as m,de as n,Z as o,R as p,$ as r,se as s,J as t,oe as u,F as v,O as w,j as x,P as y};
@@ -1 +0,0 @@
1
- import{n as e}from"./feedback-button-B99ThNJW.js";export{e as FeedbackModal};