@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.
Files changed (153) hide show
  1. package/dist/assets/{CellStatus-CRFBVGjI.js → CellStatus-Bj7PvY_u.js} +1 -1
  2. package/dist/assets/{ErrorBoundary-BKmN7zye.js → ErrorBoundary-CG6gtgym.js} +2 -2
  3. package/dist/assets/{JsonOutput-nqqzL-n0.js → JsonOutput-NVH7GWyC.js} +1 -1
  4. package/dist/assets/{MarimoErrorOutput-DCu6fm2y.js → MarimoErrorOutput-z5iFryzf.js} +1 -1
  5. package/dist/assets/{RenderHTML-DRMkieKn.js → RenderHTML-C7QcdAgB.js} +1 -1
  6. package/dist/assets/{RunButton-CPZWjCL9.js → RunButton-A__3wcpF.js} +1 -1
  7. package/dist/assets/{add-cell-with-ai-D20NyZO4.js → add-cell-with-ai-Wyt6GBhk.js} +1 -1
  8. package/dist/assets/{add-connection-dialog-pQcdKIlO.js → add-connection-dialog-bl8dqCVs.js} +1 -1
  9. package/dist/assets/{agent-panel-tNErBgmq.js → agent-panel-BcoO8S6Q.js} +1 -1
  10. package/dist/assets/{ai-model-dropdown-BIeRPKed.js → ai-model-dropdown-CvJsRj9y.js} +1 -1
  11. package/dist/assets/{api-DYL9A86_.js → api-DVLtXyp7.js} +1 -1
  12. package/dist/assets/{app-config-button-DkNGV0b1.js → app-config-button-C7QrZbqr.js} +1 -1
  13. package/dist/assets/{blob-BIYte8oT.js → blob-Bx8A10xl.js} +1 -1
  14. package/dist/assets/{cell-editor-C5RTy4Y0.js → cell-editor-CevZDS63.js} +4 -4
  15. package/dist/assets/cell-link-DC1UZQr_.js +1 -0
  16. package/dist/assets/{cells-h5u00Nz8.js → cells-CxY8RSp7.js} +2 -2
  17. package/dist/assets/{chat-display-DzTkUWMw.js → chat-display-YETh6yXV.js} +1 -1
  18. package/dist/assets/{chat-panel-CKqEpnjN.js → chat-panel-DyaePe7B.js} +1 -1
  19. package/dist/assets/{chat-ui-CGzOsYVb.js → chat-ui-D_QL46qM.js} +1 -1
  20. package/dist/assets/code-visibility-DNzJgrTz.js +1 -0
  21. package/dist/assets/{column-preview-DtrgJzVI.js → column-preview-sL_Jo0hX.js} +1 -1
  22. package/dist/assets/{command-palette-BKYGTfS5.js → command-palette-DMQFuWz1.js} +1 -1
  23. package/dist/assets/{common-BMeq0Bqi.js → common-Cy3lJDvJ.js} +1 -1
  24. package/dist/assets/{components-CWcf8098.js → components-D9ZIJGG8.js} +1 -1
  25. package/dist/assets/{config-BAnYSdpO.js → config-D_c_6ce1.js} +1 -1
  26. package/dist/assets/{datasource-BLHGFSsT.js → datasource-Tst92Pzi.js} +1 -1
  27. package/dist/assets/{dependency-graph-panel-C8X3Nn8W.js → dependency-graph-panel-BwMayTyZ.js} +1 -1
  28. package/dist/assets/{documentation-panel-C8wrSKlX.js → documentation-panel-OMUbXgKW.js} +1 -1
  29. package/dist/assets/{download-CUgiCx7s.js → download-CPMAf7re.js} +1 -1
  30. package/dist/assets/{edit-page-CqPNBpAP.js → edit-page-DD1NFkiu.js} +6 -6
  31. package/dist/assets/{error-entries-DQD4A9cx.js → error-entries-DBpwg0GC.js} +1 -1
  32. package/dist/assets/{error-panel-BJYkwYEY.js → error-panel-ByOEJuZN.js} +1 -1
  33. package/dist/assets/{feedback-button-DbTjGwcX.js → feedback-button-7KY7nL-j.js} +2 -2
  34. package/dist/assets/feedback-button-DxjoRfo3.js +1 -0
  35. package/dist/assets/{file-explorer-panel-BQi88S1o.js → file-explorer-panel-B92ugwxr.js} +1 -1
  36. package/dist/assets/{file-name-input-DPJSxW_X.js → file-name-input-DZehOHLA.js} +1 -1
  37. package/dist/assets/{floating-outline-3PlTr0K1.js → floating-outline-K7cVdOlC.js} +1 -1
  38. package/dist/assets/{gallery-page-BqvVmwde.js → gallery-page-DoriMrLH.js} +1 -1
  39. package/dist/assets/{glide-data-editor-CAa14cIr.js → glide-data-editor-DWyeHe3u.js} +1 -1
  40. package/dist/assets/{home-page-Bg-WqjiD.js → home-page-BcJJsD_Q.js} +1 -1
  41. package/dist/assets/{hooks-DLBvBo8c.js → hooks-DNPsQf79.js} +1 -1
  42. package/dist/assets/index-B7ZZVUcj.css +2 -0
  43. package/dist/assets/{index-BtV91sqJ.js → index-CqJKdR1j.js} +5 -5
  44. package/dist/assets/{layout-mWXZofX2.js → layout-BaluekG6.js} +3 -3
  45. package/dist/assets/{logs-panel-IKL6F2OX.js → logs-panel-OkoO5mbN.js} +1 -1
  46. package/dist/assets/{markdown-renderer-DHvAXF8E.js → markdown-renderer-mDlWBE1M.js} +1 -1
  47. package/dist/assets/{name-cell-input-j4eGLLyd.js → name-cell-input-D_Q2IhP9.js} +1 -1
  48. package/dist/assets/{outline-panel-CprDWmxn.js → outline-panel-Dy2cmYDF.js} +1 -1
  49. package/dist/assets/packages-panel-CIvhx0hb.js +1 -0
  50. package/dist/assets/{panel-accordion-state-8xXVhawu.js → panel-accordion-state-BI4SU22L.js} +1 -1
  51. package/dist/assets/panels-CO6Clgel.js +4 -0
  52. package/dist/assets/{process-output-DOmeJ1VR.js → process-output-Crm0vqbi.js} +1 -1
  53. package/dist/assets/{readonly-code-display-xvH6MyT6.js → readonly-code-display-CNbw6v1-.js} +1 -1
  54. package/dist/assets/{readonly-python-code-D4YGmSvB.js → readonly-python-code-DcYhdd1B.js} +1 -1
  55. package/dist/assets/{reveal-component-CDUG1x74.js → reveal-component-DeZTBbnn.js} +1 -1
  56. package/dist/assets/{run-page-BAqLgcq9.js → run-page-BNEg-Ro9.js} +1 -1
  57. package/dist/assets/runtime-details-state-lxXYwJcI.js +1 -0
  58. package/dist/assets/{scratchpad-panel-BFbUuzdn.js → scratchpad-panel-lDJWcpVs.js} +1 -1
  59. package/dist/assets/{session-panel-BdN43Lzi.js → session-panel-BqutyoYd.js} +1 -1
  60. package/dist/assets/{snippets-panel-5_jR08Zd.js → snippets-panel-YttkYAW9.js} +1 -1
  61. package/dist/assets/state-B2ZbImvm.js +1 -0
  62. package/dist/assets/{state-BxJ_FX3k.js → state-B85OOscw.js} +1 -1
  63. package/dist/assets/{state-7os84dnI.js → state-sbCCk7ZP.js} +1 -1
  64. package/dist/assets/{terminal-DHWTOXu3.js → terminal-3hxM6K9q.js} +1 -1
  65. package/dist/assets/{tracing-CRkNpBaH.js → tracing-DZYfCw6p.js} +1 -1
  66. package/dist/assets/{tracing-panel-DV2-2TUI.js → tracing-panel-BUPJ77jB.js} +2 -2
  67. package/dist/assets/{tree-actions-CShqSonp.js → tree-actions-D5clvgKl.js} +1 -1
  68. package/dist/assets/{useAddCell-DesMfnE4.js → useAddCell-Bhv6GT3m.js} +1 -1
  69. package/dist/assets/{useBoolean-BofUVeKg.js → useBoolean-Co-_MyFz.js} +1 -1
  70. package/dist/assets/{useCellActionButton-BO5SFMxA.js → useCellActionButton-DwAUf_Cn.js} +1 -1
  71. package/dist/assets/{useDeleteCell-DSbUjpSC.js → useDeleteCell-BKN7t4DH.js} +1 -1
  72. package/dist/assets/{useDependencyPanelTab-JIFiQrr2.js → useDependencyPanelTab-CbcGEmqM.js} +1 -1
  73. package/dist/assets/{useInstallPackage-CCQcFc6U.js → useInstallPackage-HTUlGg0U.js} +1 -1
  74. package/dist/assets/{useNotebookActions-CjjED3t-.js → useNotebookActions-pXzVjrlk.js} +1 -1
  75. package/dist/assets/{useRunCells-Bx3wXUWO.js → useRunCells-zMO43eNR.js} +1 -1
  76. package/dist/assets/{useSplitCell-BRmQP02b.js → useSplitCell-xEtFLByO.js} +1 -1
  77. package/dist/assets/{vega-component-C28fL5fD.js → vega-component-Chbfi__Q.js} +1 -1
  78. package/dist/index.html +24 -24
  79. package/package.json +1 -1
  80. package/src/__tests__/mount.test.ts +6 -6
  81. package/src/components/editor/actions/useRestartKernel.tsx +2 -2
  82. package/src/components/editor/alerts/__tests__/connection-notice.test.tsx +224 -17
  83. package/src/components/editor/alerts/connection-details.tsx +61 -0
  84. package/src/components/editor/alerts/connection-notice.tsx +46 -53
  85. package/src/components/editor/alerts/startup-progress.tsx +73 -129
  86. package/src/components/editor/app-container.tsx +2 -2
  87. package/src/components/editor/boundary/__tests__/ErrorBoundary.test.tsx +2 -2
  88. package/src/components/editor/cell/CreateCellButton.tsx +2 -2
  89. package/src/components/editor/cell/DeleteButton.tsx +2 -2
  90. package/src/components/editor/cell/RunButton.tsx +3 -3
  91. package/src/components/editor/cell/StopButton.tsx +2 -2
  92. package/src/components/editor/chrome/components/__tests__/feedback-button.test.tsx +4 -4
  93. package/src/components/editor/chrome/components/feedback-button.tsx +3 -3
  94. package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +8 -8
  95. package/src/components/editor/chrome/panels/__tests__/{sandbox-details.test.tsx → runtime-details.test.tsx} +94 -22
  96. package/src/components/editor/chrome/panels/__tests__/sandbox-recovery.test.tsx +4 -4
  97. package/src/components/editor/chrome/panels/packages-panel.tsx +8 -29
  98. package/src/components/editor/chrome/panels/{sandbox-details-state.ts → runtime-details-state.ts} +1 -1
  99. package/src/components/editor/chrome/panels/{sandbox-panel.tsx → runtime-panel.tsx} +38 -66
  100. package/src/components/editor/chrome/panels/runtime-status-toggle.tsx +56 -0
  101. package/src/components/editor/chrome/panels/sandbox-controller.tsx +2 -2
  102. package/src/components/editor/chrome/wrapper/app-chrome.tsx +3 -3
  103. package/src/components/editor/chrome/wrapper/footer-items/machine-stats.tsx +2 -2
  104. package/src/components/editor/controls/Controls.tsx +3 -3
  105. package/src/components/editor/controls/__tests__/notebook-menu-dropdown.test.tsx +2 -2
  106. package/src/components/editor/header/__tests__/status.test.tsx +4 -4
  107. package/src/components/editor/header/app-header.tsx +2 -2
  108. package/src/components/editor/header/status.tsx +3 -3
  109. package/src/components/ui/progress-stages.tsx +114 -0
  110. package/src/core/codemirror/lsp/__tests__/notebook-lsp.test.ts +106 -5
  111. package/src/core/codemirror/lsp/notebook-lsp.ts +16 -2
  112. package/src/core/codemirror/readonly/__tests__/extension.test.ts +6 -6
  113. package/src/core/codemirror/readonly/extension.ts +2 -2
  114. package/src/core/datasets/__tests__/data-source-discovery.test.ts +2 -2
  115. package/src/core/edit-app.tsx +5 -1
  116. package/src/core/export/hooks.ts +2 -2
  117. package/src/core/network/__tests__/connection.test.ts +3 -3
  118. package/src/core/network/connection-notice.ts +5 -5
  119. package/src/core/network/connection.ts +9 -9
  120. package/src/core/network/requests-lazy.ts +2 -2
  121. package/src/core/packages/__tests__/useSandboxController.test.tsx +2 -2
  122. package/src/core/packages/useSandboxController.ts +6 -6
  123. package/src/core/runtime/__tests__/adapter.test.ts +6 -6
  124. package/src/core/runtime/adapter.ts +7 -7
  125. package/src/core/runtime/config.ts +2 -2
  126. package/src/core/saving/filename.ts +2 -2
  127. package/src/core/saving/save-component.tsx +5 -5
  128. package/src/core/saving/useAutoSave.ts +2 -2
  129. package/src/core/websocket/__tests__/useMarimoKernelConnection.hook.test.tsx +30 -25
  130. package/src/core/websocket/__tests__/useMarimoKernelConnection.test.ts +5 -5
  131. package/src/core/websocket/connection-utils.ts +21 -21
  132. package/src/core/websocket/types.ts +8 -9
  133. package/src/core/websocket/useMarimoKernelConnection.tsx +21 -21
  134. package/src/mount.tsx +2 -2
  135. package/src/plugins/core/__test__/registerReactComponent.test.ts +55 -0
  136. package/src/plugins/core/registerReactComponent.tsx +3 -1
  137. package/src/stories/cell.stories.tsx +2 -2
  138. package/src/stories/layout/vertical/one-column.stories.tsx +5 -5
  139. package/src/stories/progress-stages.stories.tsx +62 -0
  140. package/src/stories/runtime-existing.stories.tsx +44 -0
  141. package/src/stories/runtime-maintenance.stories.tsx +40 -0
  142. package/src/stories/runtime-pixi.stories.tsx +28 -0
  143. package/src/stories/{sandbox.stories.tsx → runtime-story.tsx} +191 -152
  144. package/src/stories/runtime-uv.stories.tsx +56 -0
  145. package/dist/assets/cell-link-BeZAiE1D.js +0 -1
  146. package/dist/assets/code-visibility-B5QjA0ie.js +0 -1
  147. package/dist/assets/feedback-button-JqvnTSfN.js +0 -1
  148. package/dist/assets/index-CF8Gfz1R.css +0 -2
  149. package/dist/assets/packages-panel-CDDChziK.js +0 -1
  150. package/dist/assets/panels-CU7deLqc.js +0 -4
  151. package/dist/assets/sandbox-panel-D5bRunTM.js +0 -1
  152. package/dist/assets/state-Bo8h64Rn.js +0 -1
  153. package/src/components/editor/chrome/panels/sandbox-toggle.tsx +0 -52
@@ -1,11 +1,12 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
- import type { Meta, StoryObj } from "@storybook/react-vite";
3
- import { createStore, Provider, useAtomValue } from "jotai";
2
+ import type { Meta } from "@storybook/react-vite";
3
+ import { createStore, Provider, useAtomValue, useStore } from "jotai";
4
4
  import { useEffect, useRef, useState } from "react";
5
+ import { Button } from "@/components/ui/button";
5
6
  import { ConnectionNotice } from "@/components/editor/alerts/connection-notice";
6
7
  import PackagesPanel from "@/components/editor/chrome/panels/packages-panel";
7
8
  import { PanelSectionProvider } from "@/components/editor/chrome/panels/panel-context";
8
- import { SandboxToggle } from "@/components/editor/chrome/panels/sandbox-toggle";
9
+ import { RuntimeStatusToggle } from "@/components/editor/chrome/panels/runtime-status-toggle";
9
10
  import { SandboxController } from "@/components/editor/chrome/panels/sandbox-controller";
10
11
  import { chromeAtom } from "@/components/editor/chrome/state";
11
12
  import { Cell } from "@/components/editor/notebook-cell";
@@ -29,7 +30,7 @@ import { requestClientAtom } from "@/core/network/requests";
29
30
  import { createStaticRequests } from "@/core/network/requests-static";
30
31
  import { sandboxActionsAtom, sandboxAtom } from "@/core/packages/sandbox-state";
31
32
  import { filenameAtom } from "@/core/saving/file-state";
32
- import { WebSocketClosedReason, WebSocketState } from "@/core/websocket/types";
33
+ import { WebSocketClosedReason, ConnectionState } from "@/core/websocket/types";
33
34
  import { HTTPError } from "@/utils/errors";
34
35
 
35
36
  const manifest = `requires-python = ">=3.13"
@@ -70,13 +71,15 @@ interface Props {
70
71
  | "synced"
71
72
  | "restart-required";
72
73
  existingCells: boolean;
73
- backend: "uv" | "pixi";
74
+ backend: "uv" | "pixi" | null;
74
75
  saveResult: "success" | "failure" | "stale";
75
76
  theme: "light" | "dark";
76
77
  interactive: boolean;
78
+ manual: boolean;
79
+ detailsOpen: boolean;
77
80
  }
78
81
 
79
- function SandboxPreview({
82
+ function RuntimePreview({
80
83
  props,
81
84
  appConfig,
82
85
  reconnect,
@@ -99,7 +102,7 @@ function SandboxPreview({
99
102
  <aside className="flex flex-col w-[300px] shrink-0 border-r h-[520px]">
100
103
  <div className="flex items-center justify-between px-3 py-2 border-b text-sm">
101
104
  Packages
102
- <SandboxToggle section="sidebar" />
105
+ <RuntimeStatusToggle section="sidebar" />
103
106
  </div>
104
107
  <PanelSectionProvider value="sidebar">
105
108
  <PackagesPanel />
@@ -130,6 +133,85 @@ function SandboxPreview({
130
133
  );
131
134
  }
132
135
 
136
+ function LifecycleControls({ backend }: { backend: Props["backend"] }) {
137
+ const store = useStore();
138
+ const [position, setPosition] = useState(0);
139
+ const phases = backend
140
+ ? (["preparing", "starting", "failed", "retrying", "ready"] as const)
141
+ : (["starting", "failed", "retrying", "ready"] as const);
142
+ const next = phases[(position + 1) % phases.length];
143
+ const labels = {
144
+ preparing: "Restart walkthrough",
145
+ starting: "Start kernel",
146
+ failed: "Simulate kernel failure",
147
+ retrying: "Retry kernel",
148
+ ready: "Kernel connected",
149
+ };
150
+ const advance = () => {
151
+ setPosition((position + 1) % phases.length);
152
+ store.set(
153
+ kernelStartupErrorAtom,
154
+ next === "failed" ? "The kernel process exited before connecting." : null,
155
+ );
156
+ store.set(
157
+ connectionAtom,
158
+ next === "ready"
159
+ ? { state: ConnectionState.OPEN }
160
+ : next === "failed"
161
+ ? {
162
+ state: ConnectionState.CLOSED,
163
+ code: WebSocketClosedReason.KERNEL_STARTUP_ERROR,
164
+ phase: "starting-kernel",
165
+ reason: "Kernel failed to start",
166
+ }
167
+ : {
168
+ state: ConnectionState.CONNECTING,
169
+ phase:
170
+ next === "preparing"
171
+ ? "preparing-environment"
172
+ : "starting-kernel",
173
+ },
174
+ );
175
+ store.set(startupProgressAtom, {
176
+ phase: next === "preparing" ? "preparing-environment" : "starting-kernel",
177
+ logs:
178
+ next === "failed"
179
+ ? "Kernel process exited\n"
180
+ : next === "ready"
181
+ ? "Kernel connected\n"
182
+ : "Launching kernel process\n",
183
+ log_mode: "replace",
184
+ });
185
+ store.set(alertAtom, (value) => ({
186
+ ...value,
187
+ environments: {
188
+ ...value.environments,
189
+ kernel: {
190
+ ...value.environments.kernel,
191
+ operations: value.environments.kernel.operations.map((operation) => ({
192
+ ...operation,
193
+ status:
194
+ next === "preparing"
195
+ ? { kind: "running" }
196
+ : { kind: "succeeded" },
197
+ })),
198
+ },
199
+ },
200
+ }));
201
+ };
202
+ return (
203
+ <div className="mb-4 flex items-center gap-3 text-xs text-muted-foreground">
204
+ <Button variant="outline" size="xs" onClick={advance}>
205
+ {labels[next]}
206
+ </Button>
207
+ <span>
208
+ Story controls — advance the actual app through startup, failure, retry,
209
+ and completion.
210
+ </span>
211
+ </div>
212
+ );
213
+ }
214
+
133
215
  function OpenManifest() {
134
216
  const actions = useAtomValue(sandboxActionsAtom);
135
217
  const opened = useRef(false);
@@ -142,7 +224,7 @@ function OpenManifest() {
142
224
  return null;
143
225
  }
144
226
 
145
- function SandboxStory(props: Props) {
227
+ export function RuntimeStory(props: Props) {
146
228
  const [store] = useState(() => {
147
229
  const state = createStore();
148
230
  let currentManifest =
@@ -162,7 +244,7 @@ function SandboxStory(props: Props) {
162
244
  });
163
245
  state.set(chromeAtom, {
164
246
  selectedPanel: "packages",
165
- isSidebarOpen: props.surface !== "notebook",
247
+ isSidebarOpen: props.surface !== "notebook" || props.detailsOpen,
166
248
  isDeveloperPanelOpen: false,
167
249
  selectedDeveloperPanelTab: "errors",
168
250
  });
@@ -170,24 +252,29 @@ function SandboxStory(props: Props) {
170
252
  connectionAtom,
171
253
  props.phase === "preparing" || props.phase === "starting"
172
254
  ? {
173
- state: WebSocketState.CONNECTING,
174
- phase:
175
- props.phase === "preparing"
255
+ state: ConnectionState.CONNECTING,
256
+ phase: props.backend
257
+ ? props.phase === "preparing"
176
258
  ? "preparing-environment"
177
- : "starting-kernel",
259
+ : "starting-kernel"
260
+ : undefined,
178
261
  }
179
262
  : props.phase === "failed"
180
263
  ? {
181
- state: WebSocketState.CLOSED,
264
+ state: ConnectionState.CLOSED,
182
265
  code: WebSocketClosedReason.KERNEL_STARTUP_ERROR,
183
266
  reason: "Kernel startup failed",
184
- phase: "preparing-environment",
267
+ phase: props.backend ? "preparing-environment" : undefined,
185
268
  }
186
- : { state: WebSocketState.OPEN },
269
+ : { state: ConnectionState.OPEN },
187
270
  );
188
271
  state.set(
189
272
  kernelStartupErrorAtom,
190
- props.phase === "failed" ? resolutionError : null,
273
+ props.phase === "failed"
274
+ ? props.backend
275
+ ? resolutionError
276
+ : "Python kernel exited before connecting.\nModuleNotFoundError: No module named 'ipykernel'"
277
+ : null,
191
278
  );
192
279
  const syncStatus: EnvironmentOperation["status"] | null =
193
280
  props.phase === "syncing"
@@ -209,53 +296,56 @@ function SandboxStory(props: Props) {
209
296
  ...value.environments,
210
297
  kernel: {
211
298
  restart_required: props.phase === "restart-required",
212
- operations: [
213
- {
214
- operation_id: "story-preparation",
215
- action: "prepare",
216
- source: "kernel",
217
- status:
218
- props.phase === "failed"
219
- ? { kind: "failed", error: resolutionError }
220
- : props.phase === "preparing"
221
- ? { kind: "running" }
222
- : { kind: "succeeded" },
223
- packages: {},
224
- logs: {
225
- environment:
226
- props.phase === "failed"
227
- ? resolutionError
228
- : preparationOutput,
229
- },
230
- },
231
- ...(syncStatus
232
- ? [
233
- {
234
- operation_id: "story-sync",
235
- action: "sync" as const,
236
- source: "kernel" as const,
237
- status: syncStatus,
238
- packages: {},
239
- logs: {
240
- environment:
241
- "Reading notebook manifest\nResolving dependencies\nChecking Python version\n",
242
- },
299
+ operations: props.backend
300
+ ? [
301
+ {
302
+ operation_id: "story-preparation",
303
+ action: "prepare",
304
+ source: "kernel",
305
+ status:
306
+ props.phase === "failed"
307
+ ? { kind: "failed", error: resolutionError }
308
+ : props.phase === "preparing"
309
+ ? { kind: "running" }
310
+ : { kind: "succeeded" },
311
+ packages: {},
312
+ logs: {
313
+ environment:
314
+ props.phase === "failed"
315
+ ? resolutionError
316
+ : preparationOutput,
243
317
  },
244
- ]
245
- : []),
246
- ],
318
+ },
319
+ ...(syncStatus
320
+ ? [
321
+ {
322
+ operation_id: "story-sync",
323
+ action: "sync" as const,
324
+ source: "kernel" as const,
325
+ status: syncStatus,
326
+ packages: {},
327
+ logs: {
328
+ environment:
329
+ "Reading notebook manifest\nResolving dependencies\nChecking Python version\n",
330
+ },
331
+ },
332
+ ]
333
+ : []),
334
+ ]
335
+ : [],
247
336
  },
248
337
  },
249
338
  }));
339
+ const preparingEnvironment =
340
+ props.backend &&
341
+ (props.phase === "preparing" || props.phase === "failed");
250
342
  state.set(startupProgressAtom, {
251
- phase:
252
- props.phase !== "preparing" && props.phase !== "failed"
253
- ? "starting-kernel"
254
- : "preparing-environment",
255
- logs:
256
- props.phase !== "preparing" && props.phase !== "failed"
257
- ? "Launching kernel process\nUsing /home/user/.cache/uv/environments-v2/bike-trips-a274bddc/bin/python\nWaiting for kernel connection\n"
258
- : "",
343
+ phase: preparingEnvironment ? "preparing-environment" : "starting-kernel",
344
+ logs: preparingEnvironment
345
+ ? ""
346
+ : props.backend
347
+ ? "Launching kernel process\nWaiting for kernel connection\n"
348
+ : "Launching kernel process\nConnecting to Python runtime\nWaiting for kernel connection\n",
259
349
  log_mode: "replace",
260
350
  });
261
351
  if (props.existingCells) {
@@ -276,8 +366,16 @@ function SandboxStory(props: Props) {
276
366
  manifest: currentManifest,
277
367
  filename: "bike_trips.py",
278
368
  }),
369
+ getPackageList: async () => ({
370
+ packages: [
371
+ { name: "marimo", version: "0.24.2" },
372
+ { name: "polars", version: "1.34.0" },
373
+ ],
374
+ }),
279
375
  getDependencyTree: async () => ({
280
- context: { kind: "sandbox", backend: props.backend },
376
+ context: props.backend
377
+ ? { kind: "sandbox", backend: props.backend }
378
+ : { kind: "package-manager", name: "pip" },
281
379
  tree: {
282
380
  name: "<root>",
283
381
  version: null,
@@ -313,7 +411,7 @@ function SandboxStory(props: Props) {
313
411
  reconnect: false,
314
412
  };
315
413
  }
316
- state.set(connectionAtom, { state: WebSocketState.OPEN });
414
+ state.set(connectionAtom, { state: ConnectionState.OPEN });
317
415
  state.set(kernelStartupErrorAtom, null);
318
416
  return {
319
417
  success: true,
@@ -327,7 +425,7 @@ function SandboxStory(props: Props) {
327
425
  });
328
426
  const appConfig = AppConfigSchema.parse({ width: "full" });
329
427
  const reconnect = async () => {
330
- store.set(connectionAtom, { state: WebSocketState.OPEN });
428
+ store.set(connectionAtom, { state: ConnectionState.OPEN });
331
429
  };
332
430
  useEffect(() => {
333
431
  if (!props.interactive) {
@@ -359,12 +457,12 @@ function SandboxStory(props: Props) {
359
457
  log_mode: "replace",
360
458
  });
361
459
  store.set(connectionAtom, {
362
- state: WebSocketState.CONNECTING,
460
+ state: ConnectionState.CONNECTING,
363
461
  phase: "starting-kernel",
364
462
  });
365
463
  }, 3500),
366
464
  setTimeout(
367
- () => store.set(connectionAtom, { state: WebSocketState.OPEN }),
465
+ () => store.set(connectionAtom, { state: ConnectionState.OPEN }),
368
466
  5500,
369
467
  ),
370
468
  ];
@@ -374,7 +472,29 @@ function SandboxStory(props: Props) {
374
472
  <Provider store={store}>
375
473
  <SandboxController onReconnect={reconnect} />
376
474
  {props.surface === "manifest" && <OpenManifest />}
377
- <SandboxPreview
475
+ <header className="mb-4 max-w-2xl space-y-1">
476
+ <h1 className="text-base font-medium">
477
+ {props.backend
478
+ ? `${props.backend} sandbox environment`
479
+ : "Existing Python environment"}
480
+ </h1>
481
+ <p className="text-sm text-muted-foreground">
482
+ {props.backend
483
+ ? "marimo prepares this notebook’s isolated environment, then starts its kernel. The button keeps the sandbox name while its status dot and details change."
484
+ : "Python is already available, so only the kernel needs to connect. The button stays labeled Runtime; there is no sandbox preparation stage."}
485
+ </p>
486
+ <p className="text-xs text-muted-foreground">
487
+ {props.surface === "packages"
488
+ ? "Packages sidebar."
489
+ : props.surface === "manifest"
490
+ ? "Sandbox manifest editor."
491
+ : props.existingCells
492
+ ? "Notebook with existing cells: inline status opens Packages for stages and logs."
493
+ : "Empty notebook: startup stages appear in the notebook until the kernel connects."}
494
+ </p>
495
+ </header>
496
+ {props.manual && <LifecycleControls backend={props.backend} />}
497
+ <RuntimePreview
378
498
  props={props}
379
499
  appConfig={appConfig}
380
500
  reconnect={reconnect}
@@ -383,9 +503,8 @@ function SandboxStory(props: Props) {
383
503
  );
384
504
  }
385
505
 
386
- const meta = {
387
- title: "Sandbox/States",
388
- component: SandboxStory,
506
+ export const runtimeStoryMeta = {
507
+ component: RuntimeStory,
389
508
  parameters: { layout: "centered" },
390
509
  args: {
391
510
  surface: "notebook",
@@ -395,94 +514,14 @@ const meta = {
395
514
  saveResult: "success",
396
515
  theme: "light",
397
516
  interactive: false,
517
+ manual: false,
518
+ detailsOpen: false,
398
519
  },
399
520
  render: (args, context) => (
400
- <SandboxStory
521
+ <RuntimeStory
401
522
  key={JSON.stringify([args, context.globals.theme])}
402
523
  {...args}
403
524
  theme={context.globals.theme === "dark" ? "dark" : "light"}
404
525
  />
405
526
  ),
406
- } satisfies Meta<typeof SandboxStory>;
407
- export default meta;
408
- type Story = StoryObj<typeof meta>;
409
-
410
- export const ExistingNotebookPreparing: Story = {
411
- name: "Startup notice / preparing",
412
- };
413
- export const EmptyNotebookPreparing: Story = {
414
- name: "Empty notebook / preparing",
415
- args: { existingCells: false },
416
- };
417
- export const StartingKernel: Story = {
418
- name: "Startup notice / starting kernel",
419
- args: { phase: "starting" },
420
- };
421
- export const StartupTransition: Story = {
422
- name: "Startup / completion transition",
423
- args: { interactive: true, backend: "pixi" },
424
- };
425
- export const EmptyStartupTransition: Story = {
426
- name: "Empty notebook / completion transition",
427
- args: { interactive: true, existingCells: false, backend: "pixi" },
428
- };
429
- export const ExistingNotebookFailed: Story = {
430
- name: "Startup notice / failed",
431
- args: { phase: "failed" },
432
- };
433
- export const EmptyNotebookFailed: Story = {
434
- name: "Empty notebook / failed",
435
- args: { phase: "failed", existingCells: false },
436
- };
437
- export const PackagesPreparing: Story = {
438
- name: "Setup details / preparing",
439
- args: { surface: "packages" },
440
- };
441
- export const PackagesStarting: Story = {
442
- name: "Setup details / starting kernel",
443
- args: { surface: "packages", phase: "starting" },
444
- };
445
- export const PackagesStartupTransition: Story = {
446
- name: "Setup details / live output",
447
- args: { surface: "packages", interactive: true },
448
- };
449
- export const PackagesFailed: Story = {
450
- name: "Setup details / failed",
451
- args: { surface: "packages", phase: "failed" },
452
- };
453
- export const PackagesReady: Story = {
454
- name: "Status row / ready",
455
- args: { surface: "packages", phase: "ready" },
456
- };
457
- export const PackagesSyncing: Story = {
458
- name: "Status row / syncing",
459
- args: { surface: "packages", phase: "syncing" },
460
- };
461
- export const PackagesSyncFailed: Story = {
462
- name: "Setup details / sync failed",
463
- args: { surface: "packages", phase: "sync-failed" },
464
- };
465
- export const PackagesSynced: Story = {
466
- name: "Setup details / sync completed",
467
- args: { surface: "packages", phase: "synced" },
468
- };
469
- export const PackagesRestartRequired: Story = {
470
- name: "Setup details / restart required",
471
- args: { surface: "packages", phase: "restart-required" },
472
- };
473
- export const PixiPackagesReady: Story = {
474
- name: "Status row / pixi",
475
- args: { surface: "packages", phase: "ready", backend: "pixi" },
476
- };
477
- export const ManifestEditor: Story = {
478
- name: "Manifest / edit",
479
- args: { surface: "manifest", phase: "failed" },
480
- };
481
- export const ManifestSyncFails: Story = {
482
- name: "Manifest / failed save",
483
- args: { surface: "manifest", phase: "failed", saveResult: "failure" },
484
- };
485
- export const ManifestChangedOnDisk: Story = {
486
- name: "Manifest / changed on disk",
487
- args: { surface: "manifest", phase: "failed", saveResult: "stale" },
488
- };
527
+ } satisfies Meta<typeof RuntimeStory>;
@@ -0,0 +1,56 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+ import type { Meta, StoryObj } from "@storybook/react-vite";
3
+ import { type RuntimeStory, runtimeStoryMeta } from "./runtime-story";
4
+
5
+ const meta = {
6
+ ...runtimeStoryMeta,
7
+ title: "Runtime/uv sandbox",
8
+ args: { ...runtimeStoryMeta.args, backend: "uv" },
9
+ } satisfies Meta<typeof RuntimeStory>;
10
+ export default meta;
11
+ type Story = StoryObj<typeof meta>;
12
+
13
+ export const Walkthrough: Story = {
14
+ name: "Walkthrough",
15
+ args: { manual: true, detailsOpen: true },
16
+ };
17
+ export const NotebookPreparing: Story = {
18
+ name: "Notebook — preparing environment",
19
+ args: {},
20
+ };
21
+ export const NotebookStarting: Story = {
22
+ name: "Notebook — starting kernel",
23
+ args: { phase: "starting" },
24
+ };
25
+ export const NotebookFailed: Story = {
26
+ name: "Notebook — setup failed",
27
+ args: { phase: "failed" },
28
+ };
29
+ export const EmptyNotebook: Story = {
30
+ name: "Empty notebook — preparing",
31
+ args: { existingCells: false },
32
+ };
33
+ export const EmptyNotebookFailed: Story = {
34
+ name: "Empty notebook — setup failed",
35
+ args: { existingCells: false, phase: "failed" },
36
+ };
37
+ export const DetailsPreparing: Story = {
38
+ name: "Sidebar — preparing environment",
39
+ args: { surface: "packages" },
40
+ };
41
+ export const DetailsStarting: Story = {
42
+ name: "Sidebar — starting kernel",
43
+ args: { surface: "packages", phase: "starting" },
44
+ };
45
+ export const DetailsFailed: Story = {
46
+ name: "Sidebar — setup failed",
47
+ args: { surface: "packages", phase: "failed" },
48
+ };
49
+ export const Ready: Story = {
50
+ name: "Sidebar — ready",
51
+ args: { surface: "packages", phase: "ready" },
52
+ };
53
+ export const LiveOutput: Story = {
54
+ name: "Sidebar — live output",
55
+ args: { surface: "packages", interactive: true },
56
+ };
@@ -1 +0,0 @@
1
- import{c as e,l as t,t as n,u as r}from"./useEvent-Dc9HkKI2.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-h5u00Nz8.js";import{t as d}from"./compiler-runtime-BQ9S8nyo.js";import{p as f}from"./config-Cyum2gRs.js";import{g as p}from"./useEventListener-IRzG-0mH.js";import{i as m}from"./utils-B4K466FK.js";import{c as h}from"./session-BykGt6S3.js";import{T as g,_}from"./config-BAnYSdpO.js";import{t as v}from"./cn-ldLVeaqm.js";import{t as y}from"./jsx-runtime-B74pBk57.js";import{n as b}from"./paths-CzfpItSz.js";import{b as x}from"./write-secret-modal-BcosCmZy.js";import{r as S}from"./requests-yTHMxhVa.js";var C=d();function w(){return t(u)}function T(){let t=(0,C.c)(5),[i]=e(_),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===g.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-Dc9HkKI2.js";import{t as n}from"./compiler-runtime-BQ9S8nyo.js";import{i as r,t as i}from"./utils-B4K466FK.js";import{m as a,v as o}from"./session-BykGt6S3.js";import{M as s,T as c,_ as l,l as u}from"./config-BAnYSdpO.js";import{r as d}from"./state-YaAkDV8S.js";import{i as f,n as p,o as m}from"./state-ja7GWeCS.js";import{i as h}from"./strings-hlCLC-K1.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(l);switch(t.state){case c.OPEN:return{kind:`ready`};case c.CONNECTING:return{kind:`connecting`,progress:{label:`Connecting…`}};case c.NOT_STARTED:return{kind:`connecting`,progress:{label:`Not connected`}};case c.CLOSING:case c.CLOSED:return{kind:`failed`,error:{message:t.state===c.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 u()?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{n as e}from"./feedback-button-DbTjGwcX.js";export{e as FeedbackModal};