@marimo-team/frontend 0.25.2-dev66 → 0.25.2-dev89
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{CellStatus-CRFBVGjI.js → CellStatus-Bj7PvY_u.js} +1 -1
- package/dist/assets/{ErrorBoundary-BKmN7zye.js → ErrorBoundary-CG6gtgym.js} +2 -2
- package/dist/assets/{JsonOutput-nqqzL-n0.js → JsonOutput-NVH7GWyC.js} +1 -1
- package/dist/assets/{MarimoErrorOutput-DCu6fm2y.js → MarimoErrorOutput-z5iFryzf.js} +1 -1
- package/dist/assets/{RenderHTML-DRMkieKn.js → RenderHTML-C7QcdAgB.js} +1 -1
- package/dist/assets/{RunButton-CPZWjCL9.js → RunButton-A__3wcpF.js} +1 -1
- package/dist/assets/{add-cell-with-ai-D20NyZO4.js → add-cell-with-ai-Wyt6GBhk.js} +1 -1
- package/dist/assets/{add-connection-dialog-pQcdKIlO.js → add-connection-dialog-bl8dqCVs.js} +1 -1
- package/dist/assets/{agent-panel-tNErBgmq.js → agent-panel-BcoO8S6Q.js} +1 -1
- package/dist/assets/{ai-model-dropdown-BIeRPKed.js → ai-model-dropdown-CvJsRj9y.js} +1 -1
- package/dist/assets/{api-DYL9A86_.js → api-DVLtXyp7.js} +1 -1
- package/dist/assets/{app-config-button-DkNGV0b1.js → app-config-button-C7QrZbqr.js} +1 -1
- package/dist/assets/{blob-BIYte8oT.js → blob-Bx8A10xl.js} +1 -1
- package/dist/assets/{cell-editor-C5RTy4Y0.js → cell-editor-CevZDS63.js} +4 -4
- package/dist/assets/cell-link-DC1UZQr_.js +1 -0
- package/dist/assets/{cells-h5u00Nz8.js → cells-CxY8RSp7.js} +2 -2
- package/dist/assets/{chat-display-DzTkUWMw.js → chat-display-YETh6yXV.js} +1 -1
- package/dist/assets/{chat-panel-CKqEpnjN.js → chat-panel-DyaePe7B.js} +1 -1
- package/dist/assets/{chat-ui-CGzOsYVb.js → chat-ui-D_QL46qM.js} +1 -1
- package/dist/assets/code-visibility-DNzJgrTz.js +1 -0
- package/dist/assets/{column-preview-DtrgJzVI.js → column-preview-sL_Jo0hX.js} +1 -1
- package/dist/assets/{command-palette-BKYGTfS5.js → command-palette-DMQFuWz1.js} +1 -1
- package/dist/assets/{common-BMeq0Bqi.js → common-Cy3lJDvJ.js} +1 -1
- package/dist/assets/{components-CWcf8098.js → components-D9ZIJGG8.js} +1 -1
- package/dist/assets/{config-BAnYSdpO.js → config-D_c_6ce1.js} +1 -1
- package/dist/assets/{datasource-BLHGFSsT.js → datasource-Tst92Pzi.js} +1 -1
- package/dist/assets/{dependency-graph-panel-C8X3Nn8W.js → dependency-graph-panel-BwMayTyZ.js} +1 -1
- package/dist/assets/{documentation-panel-C8wrSKlX.js → documentation-panel-OMUbXgKW.js} +1 -1
- package/dist/assets/{download-CUgiCx7s.js → download-CPMAf7re.js} +1 -1
- package/dist/assets/{edit-page-CqPNBpAP.js → edit-page-DD1NFkiu.js} +6 -6
- package/dist/assets/{error-entries-DQD4A9cx.js → error-entries-DBpwg0GC.js} +1 -1
- package/dist/assets/{error-panel-BJYkwYEY.js → error-panel-ByOEJuZN.js} +1 -1
- package/dist/assets/{feedback-button-DbTjGwcX.js → feedback-button-7KY7nL-j.js} +2 -2
- package/dist/assets/feedback-button-DxjoRfo3.js +1 -0
- package/dist/assets/{file-explorer-panel-BQi88S1o.js → file-explorer-panel-B92ugwxr.js} +1 -1
- package/dist/assets/{file-name-input-DPJSxW_X.js → file-name-input-DZehOHLA.js} +1 -1
- package/dist/assets/{floating-outline-3PlTr0K1.js → floating-outline-K7cVdOlC.js} +1 -1
- package/dist/assets/{gallery-page-BqvVmwde.js → gallery-page-DoriMrLH.js} +1 -1
- package/dist/assets/{glide-data-editor-CAa14cIr.js → glide-data-editor-DWyeHe3u.js} +1 -1
- package/dist/assets/{home-page-Bg-WqjiD.js → home-page-BcJJsD_Q.js} +1 -1
- package/dist/assets/{hooks-DLBvBo8c.js → hooks-DNPsQf79.js} +1 -1
- package/dist/assets/index-B7ZZVUcj.css +2 -0
- package/dist/assets/{index-BtV91sqJ.js → index-CqJKdR1j.js} +5 -5
- package/dist/assets/{layout-mWXZofX2.js → layout-BaluekG6.js} +3 -3
- package/dist/assets/{logs-panel-IKL6F2OX.js → logs-panel-OkoO5mbN.js} +1 -1
- package/dist/assets/{markdown-renderer-DHvAXF8E.js → markdown-renderer-mDlWBE1M.js} +1 -1
- package/dist/assets/{name-cell-input-j4eGLLyd.js → name-cell-input-D_Q2IhP9.js} +1 -1
- package/dist/assets/{outline-panel-CprDWmxn.js → outline-panel-Dy2cmYDF.js} +1 -1
- package/dist/assets/packages-panel-CIvhx0hb.js +1 -0
- package/dist/assets/{panel-accordion-state-8xXVhawu.js → panel-accordion-state-BI4SU22L.js} +1 -1
- package/dist/assets/panels-CO6Clgel.js +4 -0
- package/dist/assets/{process-output-DOmeJ1VR.js → process-output-Crm0vqbi.js} +1 -1
- package/dist/assets/{readonly-code-display-xvH6MyT6.js → readonly-code-display-CNbw6v1-.js} +1 -1
- package/dist/assets/{readonly-python-code-D4YGmSvB.js → readonly-python-code-DcYhdd1B.js} +1 -1
- package/dist/assets/{reveal-component-CDUG1x74.js → reveal-component-DeZTBbnn.js} +1 -1
- package/dist/assets/{run-page-BAqLgcq9.js → run-page-BNEg-Ro9.js} +1 -1
- package/dist/assets/runtime-details-state-lxXYwJcI.js +1 -0
- package/dist/assets/{scratchpad-panel-BFbUuzdn.js → scratchpad-panel-lDJWcpVs.js} +1 -1
- package/dist/assets/{session-panel-BdN43Lzi.js → session-panel-BqutyoYd.js} +1 -1
- package/dist/assets/{snippets-panel-5_jR08Zd.js → snippets-panel-YttkYAW9.js} +1 -1
- package/dist/assets/state-B2ZbImvm.js +1 -0
- package/dist/assets/{state-BxJ_FX3k.js → state-B85OOscw.js} +1 -1
- package/dist/assets/{state-7os84dnI.js → state-sbCCk7ZP.js} +1 -1
- package/dist/assets/{terminal-DHWTOXu3.js → terminal-3hxM6K9q.js} +1 -1
- package/dist/assets/{tracing-CRkNpBaH.js → tracing-DZYfCw6p.js} +1 -1
- package/dist/assets/{tracing-panel-DV2-2TUI.js → tracing-panel-BUPJ77jB.js} +2 -2
- package/dist/assets/{tree-actions-CShqSonp.js → tree-actions-D5clvgKl.js} +1 -1
- package/dist/assets/{useAddCell-DesMfnE4.js → useAddCell-Bhv6GT3m.js} +1 -1
- package/dist/assets/{useBoolean-BofUVeKg.js → useBoolean-Co-_MyFz.js} +1 -1
- package/dist/assets/{useCellActionButton-BO5SFMxA.js → useCellActionButton-DwAUf_Cn.js} +1 -1
- package/dist/assets/{useDeleteCell-DSbUjpSC.js → useDeleteCell-BKN7t4DH.js} +1 -1
- package/dist/assets/{useDependencyPanelTab-JIFiQrr2.js → useDependencyPanelTab-CbcGEmqM.js} +1 -1
- package/dist/assets/{useInstallPackage-CCQcFc6U.js → useInstallPackage-HTUlGg0U.js} +1 -1
- package/dist/assets/{useNotebookActions-CjjED3t-.js → useNotebookActions-pXzVjrlk.js} +1 -1
- package/dist/assets/{useRunCells-Bx3wXUWO.js → useRunCells-zMO43eNR.js} +1 -1
- package/dist/assets/{useSplitCell-BRmQP02b.js → useSplitCell-xEtFLByO.js} +1 -1
- package/dist/assets/{vega-component-C28fL5fD.js → vega-component-Chbfi__Q.js} +1 -1
- package/dist/index.html +24 -24
- package/package.json +1 -1
- package/src/__tests__/mount.test.ts +6 -6
- package/src/components/editor/actions/useRestartKernel.tsx +2 -2
- package/src/components/editor/alerts/__tests__/connection-notice.test.tsx +224 -17
- package/src/components/editor/alerts/connection-details.tsx +61 -0
- package/src/components/editor/alerts/connection-notice.tsx +46 -53
- package/src/components/editor/alerts/startup-progress.tsx +73 -129
- package/src/components/editor/app-container.tsx +2 -2
- package/src/components/editor/boundary/__tests__/ErrorBoundary.test.tsx +2 -2
- package/src/components/editor/cell/CreateCellButton.tsx +2 -2
- package/src/components/editor/cell/DeleteButton.tsx +2 -2
- package/src/components/editor/cell/RunButton.tsx +3 -3
- package/src/components/editor/cell/StopButton.tsx +2 -2
- package/src/components/editor/chrome/components/__tests__/feedback-button.test.tsx +4 -4
- package/src/components/editor/chrome/components/feedback-button.tsx +3 -3
- package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +8 -8
- package/src/components/editor/chrome/panels/__tests__/{sandbox-details.test.tsx → runtime-details.test.tsx} +94 -22
- package/src/components/editor/chrome/panels/__tests__/sandbox-recovery.test.tsx +4 -4
- package/src/components/editor/chrome/panels/packages-panel.tsx +8 -29
- package/src/components/editor/chrome/panels/{sandbox-details-state.ts → runtime-details-state.ts} +1 -1
- package/src/components/editor/chrome/panels/{sandbox-panel.tsx → runtime-panel.tsx} +38 -66
- package/src/components/editor/chrome/panels/runtime-status-toggle.tsx +56 -0
- package/src/components/editor/chrome/panels/sandbox-controller.tsx +2 -2
- package/src/components/editor/chrome/wrapper/app-chrome.tsx +3 -3
- package/src/components/editor/chrome/wrapper/footer-items/machine-stats.tsx +2 -2
- package/src/components/editor/controls/Controls.tsx +3 -3
- package/src/components/editor/controls/__tests__/notebook-menu-dropdown.test.tsx +2 -2
- package/src/components/editor/header/__tests__/status.test.tsx +4 -4
- package/src/components/editor/header/app-header.tsx +2 -2
- package/src/components/editor/header/status.tsx +3 -3
- package/src/components/ui/progress-stages.tsx +114 -0
- package/src/core/codemirror/lsp/__tests__/notebook-lsp.test.ts +106 -5
- package/src/core/codemirror/lsp/notebook-lsp.ts +16 -2
- package/src/core/codemirror/readonly/__tests__/extension.test.ts +6 -6
- package/src/core/codemirror/readonly/extension.ts +2 -2
- package/src/core/datasets/__tests__/data-source-discovery.test.ts +2 -2
- package/src/core/edit-app.tsx +5 -1
- package/src/core/export/hooks.ts +2 -2
- package/src/core/network/__tests__/connection.test.ts +3 -3
- package/src/core/network/connection-notice.ts +5 -5
- package/src/core/network/connection.ts +9 -9
- package/src/core/network/requests-lazy.ts +2 -2
- package/src/core/packages/__tests__/useSandboxController.test.tsx +2 -2
- package/src/core/packages/useSandboxController.ts +6 -6
- package/src/core/runtime/__tests__/adapter.test.ts +6 -6
- package/src/core/runtime/adapter.ts +7 -7
- package/src/core/runtime/config.ts +2 -2
- package/src/core/saving/filename.ts +2 -2
- package/src/core/saving/save-component.tsx +5 -5
- package/src/core/saving/useAutoSave.ts +2 -2
- package/src/core/websocket/__tests__/useMarimoKernelConnection.hook.test.tsx +30 -25
- package/src/core/websocket/__tests__/useMarimoKernelConnection.test.ts +5 -5
- package/src/core/websocket/connection-utils.ts +21 -21
- package/src/core/websocket/types.ts +8 -9
- package/src/core/websocket/useMarimoKernelConnection.tsx +21 -21
- package/src/mount.tsx +2 -2
- package/src/plugins/core/__test__/registerReactComponent.test.ts +55 -0
- package/src/plugins/core/registerReactComponent.tsx +3 -1
- package/src/stories/cell.stories.tsx +2 -2
- package/src/stories/layout/vertical/one-column.stories.tsx +5 -5
- package/src/stories/progress-stages.stories.tsx +62 -0
- package/src/stories/runtime-existing.stories.tsx +44 -0
- package/src/stories/runtime-maintenance.stories.tsx +40 -0
- package/src/stories/runtime-pixi.stories.tsx +28 -0
- package/src/stories/{sandbox.stories.tsx → runtime-story.tsx} +191 -152
- package/src/stories/runtime-uv.stories.tsx +56 -0
- package/dist/assets/cell-link-BeZAiE1D.js +0 -1
- package/dist/assets/code-visibility-B5QjA0ie.js +0 -1
- package/dist/assets/feedback-button-JqvnTSfN.js +0 -1
- package/dist/assets/index-CF8Gfz1R.css +0 -2
- package/dist/assets/packages-panel-CDDChziK.js +0 -1
- package/dist/assets/panels-CU7deLqc.js +0 -4
- package/dist/assets/sandbox-panel-D5bRunTM.js +0 -1
- package/dist/assets/state-Bo8h64Rn.js +0 -1
- package/src/components/editor/chrome/panels/sandbox-toggle.tsx +0 -52
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
-
import { useAtomValue } from "jotai";
|
|
2
|
+
import { useAtom, useAtomValue } from "jotai";
|
|
3
3
|
import { ArrowRightIcon, ChevronRightIcon } from "lucide-react";
|
|
4
4
|
import { useState } from "react";
|
|
5
5
|
import { Button } from "@/components/ui/button";
|
|
@@ -8,25 +8,31 @@ import type { AppConfig } from "@/core/config/config-schema";
|
|
|
8
8
|
import { connectionAtom } from "@/core/network/connection";
|
|
9
9
|
import { useConnectionNotice } from "@/core/network/useConnectionNotice";
|
|
10
10
|
import { sandboxAtom } from "@/core/packages/sandbox-state";
|
|
11
|
-
import {
|
|
11
|
+
import { ConnectionState } from "@/core/websocket/types";
|
|
12
12
|
import { useInterval } from "@/hooks/useInterval";
|
|
13
13
|
import { cn } from "@/utils/cn";
|
|
14
|
-
import {
|
|
14
|
+
import { runtimeDetailsExpandedAtom } from "../chrome/panels/runtime-details-state";
|
|
15
15
|
import { chromeAtom, useChromeActions } from "../chrome/state";
|
|
16
16
|
import { VerticalLayoutWrapper } from "../renderers/vertical-layout/vertical-layout-wrapper";
|
|
17
|
-
import {
|
|
17
|
+
import { ConnectionDetails } from "./connection-details";
|
|
18
|
+
import { ConnectionStatusIcon, hasStartupStages } from "./startup-progress";
|
|
18
19
|
|
|
19
20
|
export const ConnectionNotice = ({
|
|
20
21
|
appConfig,
|
|
21
22
|
onRetry,
|
|
23
|
+
canOpenPackages = true,
|
|
22
24
|
}: {
|
|
23
25
|
appConfig: AppConfig;
|
|
24
26
|
onRetry: () => void;
|
|
27
|
+
canOpenPackages?: boolean;
|
|
25
28
|
}) => {
|
|
26
29
|
const notice = useConnectionNotice();
|
|
27
30
|
const connection = useAtomValue(connectionAtom);
|
|
28
31
|
const hasCells = useAtomValue(hasCellsAtom);
|
|
29
32
|
const sandbox = useAtomValue(sandboxAtom);
|
|
33
|
+
const [detailsExpanded, setDetailsExpanded] = useAtom(
|
|
34
|
+
runtimeDetailsExpandedAtom,
|
|
35
|
+
);
|
|
30
36
|
const chrome = useAtomValue(chromeAtom);
|
|
31
37
|
const packagesOpen =
|
|
32
38
|
(chrome.isSidebarOpen && chrome.selectedPanel === "packages") ||
|
|
@@ -36,48 +42,60 @@ export const ConnectionNotice = ({
|
|
|
36
42
|
|
|
37
43
|
// Reconnection uses the footer indicator while existing cells stay visible.
|
|
38
44
|
const reconnecting =
|
|
39
|
-
connection.state ===
|
|
45
|
+
connection.state === ConnectionState.CONNECTING &&
|
|
40
46
|
connection.phase === "reconnecting";
|
|
41
47
|
|
|
42
|
-
|
|
48
|
+
const openDetails = () => {
|
|
49
|
+
setDetailsExpanded(true);
|
|
50
|
+
openApplication("packages");
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
if (!notice && !sandbox?.backend) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (hasCells && canOpenPackages) {
|
|
43
58
|
const title =
|
|
44
59
|
notice?.ready && notice.kind === "startup" ? "Ready" : notice?.title;
|
|
45
60
|
return (
|
|
46
61
|
<VerticalLayoutWrapper
|
|
47
62
|
appConfig={appConfig}
|
|
48
|
-
//
|
|
63
|
+
// Sandboxes retain this row for subsequent environment syncs.
|
|
49
64
|
className="shrink-0 h-12 pb-0 sm:pb-0 print:hidden"
|
|
50
65
|
innerClassName="pb-0 sm:pb-0 pr-4"
|
|
51
66
|
>
|
|
52
67
|
{notice && !reconnecting && (
|
|
53
|
-
<section
|
|
68
|
+
<section
|
|
69
|
+
aria-label="Notebook startup"
|
|
70
|
+
className="flex items-center gap-4"
|
|
71
|
+
>
|
|
54
72
|
<button
|
|
55
73
|
type="button"
|
|
56
|
-
onClick={
|
|
74
|
+
onClick={openDetails}
|
|
57
75
|
aria-label={`${title} Open Packages`}
|
|
58
|
-
aria-expanded={packagesOpen}
|
|
76
|
+
aria-expanded={packagesOpen && detailsExpanded}
|
|
59
77
|
className="flex h-6 items-center gap-2 whitespace-nowrap text-xs text-muted-foreground hover:text-foreground rounded focus-visible:outline-2 focus-visible:outline-ring"
|
|
60
78
|
>
|
|
61
79
|
<ConnectionStatusIcon notice={notice} />
|
|
62
80
|
<output>{title}</output>
|
|
63
81
|
<ChevronRightIcon className="size-3" aria-hidden={true} />
|
|
64
82
|
</button>
|
|
83
|
+
{!notice.pending && !notice.ready && !notice.sandbox && (
|
|
84
|
+
<Button variant="text" size="xs" onClick={onRetry}>
|
|
85
|
+
Try again
|
|
86
|
+
</Button>
|
|
87
|
+
)}
|
|
65
88
|
</section>
|
|
66
89
|
)}
|
|
67
90
|
</VerticalLayoutWrapper>
|
|
68
91
|
);
|
|
69
92
|
}
|
|
70
93
|
|
|
71
|
-
if (!notice
|
|
94
|
+
if (!notice) {
|
|
72
95
|
return null;
|
|
73
96
|
}
|
|
74
97
|
|
|
75
|
-
const showSteps =
|
|
76
|
-
notice.sandbox &&
|
|
77
|
-
notice.kind === "startup" &&
|
|
78
|
-
(notice.ready ||
|
|
79
|
-
notice.phase === "preparing-environment" ||
|
|
80
|
-
notice.phase === "starting-kernel");
|
|
98
|
+
const showSteps = hasStartupStages(notice);
|
|
81
99
|
return (
|
|
82
100
|
<VerticalLayoutWrapper
|
|
83
101
|
appConfig={appConfig}
|
|
@@ -87,51 +105,26 @@ export const ConnectionNotice = ({
|
|
|
87
105
|
<section
|
|
88
106
|
aria-label="Notebook startup"
|
|
89
107
|
className={cn(
|
|
90
|
-
"text-muted-foreground",
|
|
91
|
-
hasCells
|
|
92
|
-
? "mt-4 mb-8 border rounded-lg px-5 py-5"
|
|
93
|
-
: "max-w-md mx-auto mt-16 sm:mt-24 px-4",
|
|
94
|
-
hasCells && !notice.pending && "border-(--red-7)",
|
|
108
|
+
"text-muted-foreground max-w-md mx-auto px-4",
|
|
109
|
+
hasCells ? "mt-4 mb-8" : "mt-16 sm:mt-24",
|
|
95
110
|
)}
|
|
96
111
|
>
|
|
97
|
-
{showSteps
|
|
98
|
-
<
|
|
99
|
-
) : (
|
|
100
|
-
<div role="status" className="flex items-center gap-2.5">
|
|
101
|
-
<ConnectionStatusIcon notice={notice} />
|
|
102
|
-
<h2
|
|
103
|
-
className={cn(
|
|
104
|
-
"text-base",
|
|
105
|
-
notice.pending || notice.ready
|
|
106
|
-
? "font-normal text-foreground"
|
|
107
|
-
: "font-medium text-(--red-11)",
|
|
108
|
-
)}
|
|
109
|
-
>
|
|
110
|
-
{notice.title}
|
|
111
|
-
</h2>
|
|
112
|
-
</div>
|
|
112
|
+
{!canOpenPackages && showSteps && (
|
|
113
|
+
<output className="sr-only">{notice.title}</output>
|
|
113
114
|
)}
|
|
115
|
+
<ConnectionDetails
|
|
116
|
+
notice={notice}
|
|
117
|
+
surface={canOpenPackages ? "notebook" : "sidebar"}
|
|
118
|
+
/>
|
|
114
119
|
<div className={showSteps ? "mt-8" : "ml-6.5"}>
|
|
115
|
-
{!showSteps && (
|
|
116
|
-
<p
|
|
117
|
-
className={cn(
|
|
118
|
-
"mt-3 text-sm leading-7",
|
|
119
|
-
!notice.pending && "text-foreground",
|
|
120
|
-
)}
|
|
121
|
-
>
|
|
122
|
-
{notice.description}
|
|
123
|
-
</p>
|
|
124
|
-
)}
|
|
125
|
-
{!notice.sandbox && notice.error && (
|
|
126
|
-
<SandboxErrorOutput error={notice.error} />
|
|
127
|
-
)}
|
|
128
120
|
<div
|
|
129
121
|
className={cn(
|
|
130
122
|
"flex flex-wrap items-center gap-x-6 gap-y-3",
|
|
131
123
|
showSteps ? "border-t border-border/60 pt-4" : "mt-5",
|
|
132
124
|
)}
|
|
133
125
|
>
|
|
134
|
-
{
|
|
126
|
+
{canOpenPackages &&
|
|
127
|
+
!notice.ready &&
|
|
135
128
|
!packagesOpen &&
|
|
136
129
|
(notice.sandbox || notice.pending) && (
|
|
137
130
|
<Button
|
|
@@ -141,7 +134,7 @@ export const ConnectionNotice = ({
|
|
|
141
134
|
"p-0 text-xs gap-1.5",
|
|
142
135
|
showSteps ? "h-6" : "h-auto",
|
|
143
136
|
)}
|
|
144
|
-
onClick={
|
|
137
|
+
onClick={openDetails}
|
|
145
138
|
>
|
|
146
139
|
{notice.pending ? "View setup details" : "Open Packages"}
|
|
147
140
|
<ArrowRightIcon className="size-3" aria-hidden={true} />
|
|
@@ -1,41 +1,44 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
import { useAtomValue } from "jotai";
|
|
3
|
-
import {
|
|
4
|
-
|
|
3
|
+
import {
|
|
4
|
+
ProgressStages,
|
|
5
|
+
ProgressStatusIcon,
|
|
6
|
+
type ProgressStage,
|
|
7
|
+
type ProgressState,
|
|
8
|
+
} from "@/components/ui/progress-stages";
|
|
5
9
|
import { preparationAtom } from "@/core/packages/sandbox-state";
|
|
6
10
|
import { startupProgressAtom } from "@/core/network/connection";
|
|
7
11
|
import type { DisplayConnectionNotice } from "@/core/network/useConnectionNotice";
|
|
8
|
-
import {
|
|
9
|
-
import { StartupOutput, type StartupOutputState } from "./startup-output";
|
|
12
|
+
import { StartupOutput } from "./startup-output";
|
|
10
13
|
|
|
11
14
|
const STEPS = [
|
|
12
15
|
{
|
|
13
16
|
phase: "preparing-environment",
|
|
14
17
|
titles: {
|
|
15
|
-
|
|
18
|
+
succeeded: "Environment prepared",
|
|
16
19
|
failed: "Environment setup failed",
|
|
17
|
-
|
|
20
|
+
running: "Preparing environment",
|
|
18
21
|
waiting: "Prepare environment",
|
|
19
22
|
},
|
|
20
23
|
descriptions: {
|
|
21
|
-
|
|
24
|
+
succeeded: "Python and dependencies are available.",
|
|
22
25
|
failed: "Setup could not finish.",
|
|
23
|
-
|
|
26
|
+
running: "Set up Python and dependencies.",
|
|
24
27
|
waiting: "Set up Python and dependencies.",
|
|
25
28
|
},
|
|
26
29
|
},
|
|
27
30
|
{
|
|
28
31
|
phase: "starting-kernel",
|
|
29
32
|
titles: {
|
|
30
|
-
|
|
33
|
+
succeeded: "Kernel started",
|
|
31
34
|
failed: "Kernel failed to start",
|
|
32
|
-
|
|
35
|
+
running: "Starting kernel",
|
|
33
36
|
waiting: "Start kernel",
|
|
34
37
|
},
|
|
35
38
|
descriptions: {
|
|
36
|
-
|
|
39
|
+
succeeded: "The notebook is ready to run.",
|
|
37
40
|
failed: "The kernel could not connect.",
|
|
38
|
-
|
|
41
|
+
running: "Connecting to this notebook.",
|
|
39
42
|
waiting: "After the environment is ready.",
|
|
40
43
|
},
|
|
41
44
|
},
|
|
@@ -46,31 +49,13 @@ interface StartupProgressProps {
|
|
|
46
49
|
surface: "notebook" | "sidebar";
|
|
47
50
|
}
|
|
48
51
|
|
|
49
|
-
function
|
|
50
|
-
phase,
|
|
51
|
-
state,
|
|
52
|
-
}: {
|
|
53
|
-
phase: (typeof STEPS)[number]["phase"];
|
|
54
|
-
state: StartupOutputState;
|
|
55
|
-
}) {
|
|
56
|
-
const preparation = useAtomValue(preparationAtom);
|
|
57
|
-
const progress = useAtomValue(startupProgressAtom);
|
|
58
|
-
const preparing = phase === "preparing-environment";
|
|
59
|
-
const logs = preparing
|
|
60
|
-
? (preparation?.logs.environment ?? "")
|
|
61
|
-
: progress?.phase === phase
|
|
62
|
-
? progress.logs
|
|
63
|
-
: "";
|
|
64
|
-
|
|
52
|
+
export function hasStartupStages(notice: DisplayConnectionNotice): boolean {
|
|
65
53
|
return (
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
preparing ? "environment preparation output" : "kernel startup output"
|
|
72
|
-
}
|
|
73
|
-
/>
|
|
54
|
+
notice.kind === "startup" &&
|
|
55
|
+
(!notice.sandbox ||
|
|
56
|
+
notice.ready ||
|
|
57
|
+
notice.phase === "preparing-environment" ||
|
|
58
|
+
notice.phase === "starting-kernel")
|
|
74
59
|
);
|
|
75
60
|
}
|
|
76
61
|
|
|
@@ -85,9 +70,13 @@ function StartupSummary({ notice }: { notice: DisplayConnectionNotice }) {
|
|
|
85
70
|
</h2>
|
|
86
71
|
<p className="text-muted-foreground mt-3 text-sm leading-6">
|
|
87
72
|
{notice.ready
|
|
88
|
-
?
|
|
73
|
+
? notice.sandbox
|
|
74
|
+
? "Your environment and kernel are ready."
|
|
75
|
+
: "Your kernel is ready."
|
|
89
76
|
: notice.pending
|
|
90
|
-
?
|
|
77
|
+
? notice.sandbox
|
|
78
|
+
? "Preparing Python and dependencies for this notebook."
|
|
79
|
+
: "Connecting to this notebook’s Python kernel."
|
|
91
80
|
: "Review the setup details below to continue."}
|
|
92
81
|
</p>
|
|
93
82
|
</header>
|
|
@@ -99,106 +88,61 @@ export function ConnectionStatusIcon({
|
|
|
99
88
|
}: {
|
|
100
89
|
notice: DisplayConnectionNotice;
|
|
101
90
|
}) {
|
|
102
|
-
if (notice.ready) {
|
|
103
|
-
return (
|
|
104
|
-
<CheckIcon
|
|
105
|
-
className="size-4 shrink-0 text-(--grass-11)"
|
|
106
|
-
aria-hidden={true}
|
|
107
|
-
/>
|
|
108
|
-
);
|
|
109
|
-
}
|
|
110
|
-
if (notice.pending) {
|
|
111
|
-
return (
|
|
112
|
-
<Spinner
|
|
113
|
-
className="size-4 shrink-0 motion-reduce:animate-none"
|
|
114
|
-
aria-hidden={true}
|
|
115
|
-
/>
|
|
116
|
-
);
|
|
117
|
-
}
|
|
118
91
|
return (
|
|
119
|
-
<
|
|
120
|
-
|
|
121
|
-
aria-hidden={true}
|
|
92
|
+
<ProgressStatusIcon
|
|
93
|
+
state={notice.ready ? "succeeded" : notice.pending ? "running" : "failed"}
|
|
122
94
|
/>
|
|
123
95
|
);
|
|
124
96
|
}
|
|
125
97
|
|
|
126
98
|
export function StartupProgress({ notice, surface }: StartupProgressProps) {
|
|
99
|
+
const preparation = useAtomValue(preparationAtom);
|
|
100
|
+
const progress = useAtomValue(startupProgressAtom);
|
|
101
|
+
const activePhase = notice.sandbox ? notice.phase : "starting-kernel";
|
|
102
|
+
const stages: ProgressStage[] = STEPS.filter(
|
|
103
|
+
(step) => notice.sandbox || step.phase === "starting-kernel",
|
|
104
|
+
).map(({ phase, titles, descriptions }) => {
|
|
105
|
+
const preparing = phase === "preparing-environment";
|
|
106
|
+
const state: ProgressState =
|
|
107
|
+
notice.ready || (preparing && activePhase === "starting-kernel")
|
|
108
|
+
? "succeeded"
|
|
109
|
+
: activePhase === phase
|
|
110
|
+
? notice.pending
|
|
111
|
+
? "running"
|
|
112
|
+
: "failed"
|
|
113
|
+
: "waiting";
|
|
114
|
+
const logs = preparing
|
|
115
|
+
? (preparation?.logs.environment ?? "")
|
|
116
|
+
: progress?.phase === phase
|
|
117
|
+
? progress.logs
|
|
118
|
+
: "";
|
|
119
|
+
return {
|
|
120
|
+
id: phase,
|
|
121
|
+
state,
|
|
122
|
+
title: titles[state],
|
|
123
|
+
description: descriptions[state],
|
|
124
|
+
details: surface === "sidebar" && (
|
|
125
|
+
<StartupOutput
|
|
126
|
+
key={preparation?.operation_id}
|
|
127
|
+
logs={logs}
|
|
128
|
+
state={state === "waiting" ? "succeeded" : state}
|
|
129
|
+
label={
|
|
130
|
+
preparing
|
|
131
|
+
? "environment preparation output"
|
|
132
|
+
: "kernel startup output"
|
|
133
|
+
}
|
|
134
|
+
/>
|
|
135
|
+
),
|
|
136
|
+
};
|
|
137
|
+
});
|
|
127
138
|
return (
|
|
128
139
|
<>
|
|
129
140
|
{surface === "notebook" && <StartupSummary notice={notice} />}
|
|
130
|
-
<
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
const complete =
|
|
136
|
-
notice.ready || (index === 0 && notice.phase === "starting-kernel");
|
|
137
|
-
const active = !notice.ready && notice.phase === phase;
|
|
138
|
-
const failed = active && !notice.pending;
|
|
139
|
-
const state = complete
|
|
140
|
-
? "complete"
|
|
141
|
-
: failed
|
|
142
|
-
? "failed"
|
|
143
|
-
: active
|
|
144
|
-
? "pending"
|
|
145
|
-
: "waiting";
|
|
146
|
-
return (
|
|
147
|
-
<li
|
|
148
|
-
key={phase}
|
|
149
|
-
aria-current={active ? "step" : undefined}
|
|
150
|
-
className="relative flex items-start gap-3"
|
|
151
|
-
>
|
|
152
|
-
{index === 0 && (
|
|
153
|
-
<span
|
|
154
|
-
aria-hidden={true}
|
|
155
|
-
className="absolute left-2 top-6 -bottom-4 border-l border-border"
|
|
156
|
-
/>
|
|
157
|
-
)}
|
|
158
|
-
<span
|
|
159
|
-
className={cn(
|
|
160
|
-
"mt-0.5",
|
|
161
|
-
complete ? "text-(--grass-11)" : "text-muted-foreground",
|
|
162
|
-
)}
|
|
163
|
-
>
|
|
164
|
-
{complete ? (
|
|
165
|
-
<CheckIcon className="size-4" aria-hidden={true} />
|
|
166
|
-
) : active ? (
|
|
167
|
-
<ConnectionStatusIcon notice={notice} />
|
|
168
|
-
) : (
|
|
169
|
-
<CircleIcon
|
|
170
|
-
className="size-4 text-muted-foreground/50"
|
|
171
|
-
aria-hidden={true}
|
|
172
|
-
/>
|
|
173
|
-
)}
|
|
174
|
-
</span>
|
|
175
|
-
<div className="min-w-0 flex-1">
|
|
176
|
-
<div
|
|
177
|
-
className={cn(
|
|
178
|
-
"text-sm",
|
|
179
|
-
failed
|
|
180
|
-
? "text-(--red-11)"
|
|
181
|
-
: complete || active
|
|
182
|
-
? "text-foreground"
|
|
183
|
-
: "text-muted-foreground",
|
|
184
|
-
)}
|
|
185
|
-
>
|
|
186
|
-
{titles[state]}
|
|
187
|
-
</div>
|
|
188
|
-
<p className="mt-1 text-xs text-muted-foreground">
|
|
189
|
-
{descriptions[state]}
|
|
190
|
-
</p>
|
|
191
|
-
{surface === "sidebar" && (
|
|
192
|
-
<StartupStepOutput
|
|
193
|
-
phase={phase}
|
|
194
|
-
state={failed ? "failed" : active ? "running" : "succeeded"}
|
|
195
|
-
/>
|
|
196
|
-
)}
|
|
197
|
-
</div>
|
|
198
|
-
</li>
|
|
199
|
-
);
|
|
200
|
-
})}
|
|
201
|
-
</ol>
|
|
141
|
+
<ProgressStages
|
|
142
|
+
stages={stages}
|
|
143
|
+
label="Notebook startup stages"
|
|
144
|
+
className={surface === "notebook" ? "mt-8" : undefined}
|
|
145
|
+
/>
|
|
202
146
|
</>
|
|
203
147
|
);
|
|
204
148
|
}
|
|
@@ -7,7 +7,7 @@ import { PyodideLoader } from "@/core/wasm/PyodideLoader";
|
|
|
7
7
|
import {
|
|
8
8
|
type ConnectionStatus,
|
|
9
9
|
WebSocketClosedReason,
|
|
10
|
-
|
|
10
|
+
ConnectionState,
|
|
11
11
|
} from "@/core/websocket/types";
|
|
12
12
|
import { cn } from "@/utils/cn";
|
|
13
13
|
import { DynamicFavicon } from "./dynamic-favicon";
|
|
@@ -47,7 +47,7 @@ export const AppContainer: React.FC<PropsWithChildren<Props>> = ({
|
|
|
47
47
|
data-connection-state={connectionState}
|
|
48
48
|
className={cn(
|
|
49
49
|
"mathjax_ignore",
|
|
50
|
-
connection.state ===
|
|
50
|
+
connection.state === ConnectionState.CLOSED &&
|
|
51
51
|
connection.code !==
|
|
52
52
|
WebSocketClosedReason.KERNEL_STARTUP_ERROR &&
|
|
53
53
|
"disconnected",
|
|
@@ -14,7 +14,7 @@ import { requestClientAtom } from "@/core/network/requests";
|
|
|
14
14
|
import type { EnvironmentInfo } from "@/core/network/types";
|
|
15
15
|
import { filenameAtom } from "@/core/saving/file-state";
|
|
16
16
|
import { store } from "@/core/state/jotai";
|
|
17
|
-
import {
|
|
17
|
+
import { ConnectionState } from "@/core/websocket/types";
|
|
18
18
|
import { ErrorBoundary } from "../ErrorBoundary";
|
|
19
19
|
|
|
20
20
|
vi.mock("@/utils/copy", () => ({
|
|
@@ -144,7 +144,7 @@ function resetSharedState() {
|
|
|
144
144
|
store.set(requestClientAtom, null);
|
|
145
145
|
store.set(notebookAtom, initialNotebookState());
|
|
146
146
|
store.set(viewStateAtom, { mode: "edit", cellAnchor: null });
|
|
147
|
-
store.set(connectionAtom, { state:
|
|
147
|
+
store.set(connectionAtom, { state: ConnectionState.OPEN });
|
|
148
148
|
store.set(filenameAtom, "/project/example.py");
|
|
149
149
|
}
|
|
150
150
|
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
getConnectionTooltip,
|
|
18
18
|
isAppInteractionDisabled,
|
|
19
19
|
} from "@/core/websocket/connection-utils";
|
|
20
|
-
import type {
|
|
20
|
+
import type { ConnectionState } from "@/core/websocket/types";
|
|
21
21
|
import { cn } from "@/utils/cn";
|
|
22
22
|
import { Tooltip } from "../../ui/tooltip";
|
|
23
23
|
import { MarkdownIcon, PythonIcon } from "./code/icons";
|
|
@@ -28,7 +28,7 @@ export const CreateCellButton = ({
|
|
|
28
28
|
tooltipContent,
|
|
29
29
|
oneClickShortcut,
|
|
30
30
|
}: {
|
|
31
|
-
connectionState:
|
|
31
|
+
connectionState: ConnectionState;
|
|
32
32
|
tooltipContent: React.ReactNode;
|
|
33
33
|
onClick: ((opts: { code: string; hideCode?: boolean }) => void) | undefined;
|
|
34
34
|
oneClickShortcut: "shift" | "mod";
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
getConnectionTooltip,
|
|
7
7
|
isAppInteractionDisabled,
|
|
8
8
|
} from "@/core/websocket/connection-utils";
|
|
9
|
-
import type {
|
|
9
|
+
import type { ConnectionState } from "@/core/websocket/types";
|
|
10
10
|
import { cn } from "@/utils/cn";
|
|
11
11
|
import { Events } from "@/utils/events";
|
|
12
12
|
import { Button } from "../../ui/button";
|
|
@@ -14,7 +14,7 @@ import { Tooltip } from "../../ui/tooltip";
|
|
|
14
14
|
|
|
15
15
|
export const DeleteButton = (props: {
|
|
16
16
|
status: RuntimeState;
|
|
17
|
-
connectionState:
|
|
17
|
+
connectionState: ConnectionState;
|
|
18
18
|
onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
|
19
19
|
}): JSX.Element => {
|
|
20
20
|
const { status, connectionState, onClick } = props;
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
getConnectionTooltip,
|
|
7
7
|
isAppInteractionDisabled,
|
|
8
8
|
} from "@/core/websocket/connection-utils";
|
|
9
|
-
import type {
|
|
9
|
+
import type { ConnectionState } from "@/core/websocket/types";
|
|
10
10
|
import { renderShortcut } from "../../shortcuts/renderShortcut";
|
|
11
11
|
import { ToolbarItem } from "./toolbar";
|
|
12
12
|
|
|
@@ -16,7 +16,7 @@ function computeColor({
|
|
|
16
16
|
loading,
|
|
17
17
|
inactive,
|
|
18
18
|
}: {
|
|
19
|
-
connectionState:
|
|
19
|
+
connectionState: ConnectionState;
|
|
20
20
|
needsRun: boolean;
|
|
21
21
|
loading: boolean;
|
|
22
22
|
inactive: boolean;
|
|
@@ -37,7 +37,7 @@ export const RunButton = (props: {
|
|
|
37
37
|
edited: boolean;
|
|
38
38
|
status: RuntimeState;
|
|
39
39
|
needsRun: boolean;
|
|
40
|
-
connectionState:
|
|
40
|
+
connectionState: ConnectionState;
|
|
41
41
|
config: CellConfig;
|
|
42
42
|
onClick?: () => void;
|
|
43
43
|
}): JSX.Element => {
|
|
@@ -4,7 +4,7 @@ import type { JSX } from "react";
|
|
|
4
4
|
import { useRequestClient } from "@/core/network/requests";
|
|
5
5
|
import type { RuntimeState } from "@/core/network/types";
|
|
6
6
|
import { isAppInteractionDisabled } from "@/core/websocket/connection-utils";
|
|
7
|
-
import type {
|
|
7
|
+
import type { ConnectionState } from "@/core/websocket/types";
|
|
8
8
|
import { Functions } from "@/utils/functions";
|
|
9
9
|
import { renderShortcut } from "../../shortcuts/renderShortcut";
|
|
10
10
|
import { ToolbarItem } from "./toolbar";
|
|
@@ -12,7 +12,7 @@ import { useShouldShowInterrupt } from "./useShouldShowInterrupt";
|
|
|
12
12
|
|
|
13
13
|
export const StopButton = (props: {
|
|
14
14
|
status: RuntimeState;
|
|
15
|
-
connectionState:
|
|
15
|
+
connectionState: ConnectionState;
|
|
16
16
|
}): JSX.Element => {
|
|
17
17
|
const { connectionState, status } = props;
|
|
18
18
|
const { sendInterrupt } = useRequestClient();
|
|
@@ -14,7 +14,7 @@ import { requestClientAtom } from "@/core/network/requests";
|
|
|
14
14
|
import type { EnvironmentInfo } from "@/core/network/types";
|
|
15
15
|
import { filenameAtom } from "@/core/saving/file-state";
|
|
16
16
|
import { store } from "@/core/state/jotai";
|
|
17
|
-
import {
|
|
17
|
+
import { ConnectionState } from "@/core/websocket/types";
|
|
18
18
|
import * as copyModule from "@/utils/copy";
|
|
19
19
|
import { FeedbackModal } from "../feedback-button";
|
|
20
20
|
|
|
@@ -53,7 +53,7 @@ describe("FeedbackModal issue reporting", () => {
|
|
|
53
53
|
localStorage.clear();
|
|
54
54
|
store.set(requestClientAtom, MockRequestClient.create());
|
|
55
55
|
store.set(viewStateAtom, { mode: "edit", cellAnchor: null });
|
|
56
|
-
store.set(connectionAtom, { state:
|
|
56
|
+
store.set(connectionAtom, { state: ConnectionState.OPEN });
|
|
57
57
|
store.set(filenameAtom, "/project/example.py");
|
|
58
58
|
});
|
|
59
59
|
|
|
@@ -156,7 +156,7 @@ describe("FeedbackModal issue reporting", () => {
|
|
|
156
156
|
});
|
|
157
157
|
|
|
158
158
|
it("disables include code and explains why when disconnected", async () => {
|
|
159
|
-
store.set(connectionAtom, { state:
|
|
159
|
+
store.set(connectionAtom, { state: ConnectionState.CONNECTING });
|
|
160
160
|
store.set(
|
|
161
161
|
requestClientAtom,
|
|
162
162
|
MockRequestClient.create({
|
|
@@ -244,7 +244,7 @@ describe("FeedbackModal issue reporting", () => {
|
|
|
244
244
|
"marimo:issue-report:include-code",
|
|
245
245
|
JSON.stringify(true),
|
|
246
246
|
);
|
|
247
|
-
store.set(connectionAtom, { state:
|
|
247
|
+
store.set(connectionAtom, { state: ConnectionState.CONNECTING });
|
|
248
248
|
store.set(requestClientAtom, null);
|
|
249
249
|
render(<FeedbackModal onClose={vi.fn()} />, { wrapper });
|
|
250
250
|
|
|
@@ -39,7 +39,7 @@ import { connectionAtom } from "@/core/network/connection";
|
|
|
39
39
|
import { requestClientAtom } from "@/core/network/requests";
|
|
40
40
|
import { filenameAtom } from "@/core/saving/file-state";
|
|
41
41
|
import { store } from "@/core/state/jotai";
|
|
42
|
-
import {
|
|
42
|
+
import { ConnectionState } from "@/core/websocket/types";
|
|
43
43
|
import { useAsyncData } from "@/hooks/useAsyncData";
|
|
44
44
|
import { useLocalStorage } from "@/hooks/useLocalStorage";
|
|
45
45
|
import { cn } from "@/utils/cn";
|
|
@@ -78,7 +78,7 @@ function getNotebookSourceUnavailableReason(args: {
|
|
|
78
78
|
hasRequestClient: boolean;
|
|
79
79
|
filename: string | null;
|
|
80
80
|
codeAvailable: boolean;
|
|
81
|
-
connectionState:
|
|
81
|
+
connectionState: ConnectionState;
|
|
82
82
|
}): string | undefined {
|
|
83
83
|
if (!args.hasRequestClient) {
|
|
84
84
|
return "Notebook source is unavailable.";
|
|
@@ -89,7 +89,7 @@ function getNotebookSourceUnavailableReason(args: {
|
|
|
89
89
|
if (!args.codeAvailable) {
|
|
90
90
|
return "Notebook source is hidden in this view.";
|
|
91
91
|
}
|
|
92
|
-
if (args.connectionState !==
|
|
92
|
+
if (args.connectionState !== ConnectionState.OPEN) {
|
|
93
93
|
return "Connect the notebook to include its source.";
|
|
94
94
|
}
|
|
95
95
|
return undefined;
|