@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
|
@@ -22,10 +22,10 @@ import { withPackageInvalidation } from "@/core/packages/package-data";
|
|
|
22
22
|
import { useInstallPackages } from "@/core/packages/useInstallPackage";
|
|
23
23
|
import { sandboxAtom, sandboxSyncAtom } from "@/core/packages/sandbox-state";
|
|
24
24
|
import { store } from "@/core/state/jotai";
|
|
25
|
-
import {
|
|
25
|
+
import { ConnectionState } from "@/core/websocket/types";
|
|
26
26
|
import PackagesPanel from "../packages-panel";
|
|
27
27
|
import { PanelSectionProvider } from "../panel-context";
|
|
28
|
-
import {
|
|
28
|
+
import { RuntimeStatusToggle } from "../runtime-status-toggle";
|
|
29
29
|
|
|
30
30
|
const { openSettings } = vi.hoisted(() => ({
|
|
31
31
|
openSettings: vi.fn(),
|
|
@@ -71,7 +71,7 @@ function renderPanel(
|
|
|
71
71
|
? { backend: context.backend, manifest: "", filename: "notebook.py" }
|
|
72
72
|
: null,
|
|
73
73
|
);
|
|
74
|
-
store.set(connectionAtom, { state:
|
|
74
|
+
store.set(connectionAtom, { state: ConnectionState.OPEN });
|
|
75
75
|
store.set(sandboxSyncAtom, { kind: "succeeded" });
|
|
76
76
|
const getPackageList = vi.fn().mockResolvedValue({ packages: [] });
|
|
77
77
|
const client = MockRequestClient.create({
|
|
@@ -87,7 +87,7 @@ function renderPanel(
|
|
|
87
87
|
<Provider store={store}>
|
|
88
88
|
<TooltipProvider>
|
|
89
89
|
<PanelSectionProvider value="sidebar">
|
|
90
|
-
<
|
|
90
|
+
<RuntimeStatusToggle section="sidebar" />
|
|
91
91
|
<PackagesPanel />
|
|
92
92
|
</PanelSectionProvider>
|
|
93
93
|
</TooltipProvider>
|
|
@@ -186,7 +186,7 @@ it("refreshes an open panel when a package is installed elsewhere, after install
|
|
|
186
186
|
manifest: "",
|
|
187
187
|
filename: "notebook.py",
|
|
188
188
|
});
|
|
189
|
-
store.set(connectionAtom, { state:
|
|
189
|
+
store.set(connectionAtom, { state: ConnectionState.OPEN });
|
|
190
190
|
store.set(sandboxSyncAtom, { kind: "succeeded" });
|
|
191
191
|
store.set(requestClientAtom, withPackageInvalidation(client));
|
|
192
192
|
function InstallElsewhere() {
|
|
@@ -201,7 +201,7 @@ it("refreshes an open panel when a package is installed elsewhere, after install
|
|
|
201
201
|
<Provider store={store}>
|
|
202
202
|
<TooltipProvider>
|
|
203
203
|
<PanelSectionProvider value="sidebar">
|
|
204
|
-
<
|
|
204
|
+
<RuntimeStatusToggle section="sidebar" />
|
|
205
205
|
<PackagesPanel />
|
|
206
206
|
</PanelSectionProvider>
|
|
207
207
|
<InstallElsewhere />
|
|
@@ -221,7 +221,7 @@ it("refreshes an open panel when a package is installed elsewhere, after install
|
|
|
221
221
|
});
|
|
222
222
|
|
|
223
223
|
it("keeps packages and the install draft visible while blocking mutations during sync", async () => {
|
|
224
|
-
store.set(connectionAtom, { state:
|
|
224
|
+
store.set(connectionAtom, { state: ConnectionState.OPEN });
|
|
225
225
|
renderPanel({ kind: "sandbox", backend: "pixi" }, populatedTree);
|
|
226
226
|
await screen.findByRole("treeitem", { name: /polars/ });
|
|
227
227
|
const input = screen.getByPlaceholderText("Add packages to pixi sandbox...");
|
|
@@ -235,7 +235,7 @@ it("keeps packages and the install draft visible while blocking mutations during
|
|
|
235
235
|
expect(
|
|
236
236
|
screen.queryByRole("list", { name: "Notebook startup stages" }),
|
|
237
237
|
).not.toBeInTheDocument();
|
|
238
|
-
const toggle = screen.getByRole("button", { name:
|
|
238
|
+
const toggle = screen.getByRole("button", { name: /^pixi sandbox, / });
|
|
239
239
|
fireEvent.click(toggle);
|
|
240
240
|
expect(toggle).toHaveAttribute("aria-expanded", "false");
|
|
241
241
|
act(() =>
|
|
@@ -11,10 +11,10 @@ import { kernelStartupErrorAtom } from "@/core/errors/state";
|
|
|
11
11
|
import { connectionAtom, startupProgressAtom } from "@/core/network/connection";
|
|
12
12
|
import { requestClientAtom } from "@/core/network/requests";
|
|
13
13
|
import { sandboxAtom } from "@/core/packages/sandbox-state";
|
|
14
|
-
import { WebSocketClosedReason,
|
|
14
|
+
import { WebSocketClosedReason, ConnectionState } from "@/core/websocket/types";
|
|
15
15
|
import PackagesPanel from "../packages-panel";
|
|
16
16
|
import { PanelSectionProvider } from "../panel-context";
|
|
17
|
-
import {
|
|
17
|
+
import { RuntimeStatusToggle } from "../runtime-status-toggle";
|
|
18
18
|
|
|
19
19
|
vi.mock("@/utils/reload-safe", () => ({ reloadSafe: vi.fn() }));
|
|
20
20
|
|
|
@@ -43,8 +43,8 @@ function mount({
|
|
|
43
43
|
store.set(
|
|
44
44
|
connectionAtom,
|
|
45
45
|
preparing
|
|
46
|
-
? { state:
|
|
47
|
-
: { state:
|
|
46
|
+
? { state: ConnectionState.CONNECTING, phase: "preparing-environment" }
|
|
47
|
+
: { state: ConnectionState.OPEN },
|
|
48
48
|
);
|
|
49
49
|
store.set(alertAtom, (state) => ({
|
|
50
50
|
...state,
|
|
@@ -89,7 +89,7 @@ function mount({
|
|
|
89
89
|
<Provider store={store}>
|
|
90
90
|
<TooltipProvider>
|
|
91
91
|
<ModalProvider>
|
|
92
|
-
<
|
|
92
|
+
<RuntimeStatusToggle section="sidebar" />
|
|
93
93
|
<PanelSectionProvider value="sidebar">
|
|
94
94
|
<PackagesPanel />
|
|
95
95
|
</PanelSectionProvider>
|
|
@@ -103,7 +103,7 @@ function mount({
|
|
|
103
103
|
it("opens restored preparation and kernel logs without reopening completed startup on refresh", async () => {
|
|
104
104
|
mount();
|
|
105
105
|
await screen.findByText("numpy");
|
|
106
|
-
const toggle = screen.getByRole("button", { name:
|
|
106
|
+
const toggle = screen.getByRole("button", { name: /^uv sandbox, / });
|
|
107
107
|
expect(toggle).toHaveAttribute("aria-expanded", "false");
|
|
108
108
|
expect(
|
|
109
109
|
screen.queryByRole("region", { name: "Sandbox details" }),
|
|
@@ -164,7 +164,9 @@ it("restores a restart-required sync with the kernel restart action", async () =
|
|
|
164
164
|
})),
|
|
165
165
|
);
|
|
166
166
|
await screen.findByText("numpy");
|
|
167
|
-
expect(
|
|
167
|
+
expect(
|
|
168
|
+
screen.getByRole("heading", { name: "Sandbox restart required" }),
|
|
169
|
+
).toBeVisible();
|
|
168
170
|
expect(
|
|
169
171
|
screen.queryByRole("button", { name: "Retry sync" }),
|
|
170
172
|
).not.toBeInTheDocument();
|
|
@@ -194,7 +196,9 @@ it("restores a restart-required sync with the kernel restart action", async () =
|
|
|
194
196
|
},
|
|
195
197
|
})),
|
|
196
198
|
);
|
|
197
|
-
expect(
|
|
199
|
+
expect(
|
|
200
|
+
screen.getByRole("heading", { name: "Sandbox restart required" }),
|
|
201
|
+
).toBeVisible();
|
|
198
202
|
fireEvent.click(screen.getByRole("button", { name: "Restart Kernel" }));
|
|
199
203
|
expect(screen.getByRole("alertdialog")).toHaveTextContent("Restart Kernel");
|
|
200
204
|
expect(client.sendRestart).not.toHaveBeenCalled();
|
|
@@ -251,7 +255,7 @@ it("keeps sync output open through completion and restores it quietly after refr
|
|
|
251
255
|
unmount();
|
|
252
256
|
const refreshed = mount();
|
|
253
257
|
act(() => refreshed.store.set(alertAtom, snapshot));
|
|
254
|
-
const toggle = screen.getByRole("button", { name:
|
|
258
|
+
const toggle = screen.getByRole("button", { name: /^uv sandbox, / });
|
|
255
259
|
expect(toggle).toHaveAttribute("aria-expanded", "false");
|
|
256
260
|
fireEvent.click(toggle);
|
|
257
261
|
fireEvent.click(
|
|
@@ -266,7 +270,7 @@ it.each([false, true])(
|
|
|
266
270
|
"retains the progression after completion with inspecting=%s",
|
|
267
271
|
async (inspecting) => {
|
|
268
272
|
const { store, client } = mount({ preparing: true });
|
|
269
|
-
const toggle = screen.getByRole("button", { name:
|
|
273
|
+
const toggle = screen.getByRole("button", { name: /^uv sandbox, / });
|
|
270
274
|
expect(toggle).toHaveAttribute("aria-expanded", "true");
|
|
271
275
|
expect(client.getDependencyTree).not.toHaveBeenCalled();
|
|
272
276
|
if (inspecting) {
|
|
@@ -283,11 +287,11 @@ it.each([false, true])(
|
|
|
283
287
|
log_mode: "replace",
|
|
284
288
|
});
|
|
285
289
|
store.set(connectionAtom, {
|
|
286
|
-
state:
|
|
290
|
+
state: ConnectionState.CONNECTING,
|
|
287
291
|
phase: "starting-kernel",
|
|
288
292
|
});
|
|
289
293
|
});
|
|
290
|
-
act(() => store.set(connectionAtom, { state:
|
|
294
|
+
act(() => store.set(connectionAtom, { state: ConnectionState.OPEN }));
|
|
291
295
|
await screen.findByText("numpy");
|
|
292
296
|
expect(toggle).toHaveAttribute("aria-expanded", String(inspecting));
|
|
293
297
|
if (!inspecting) {
|
|
@@ -308,7 +312,7 @@ it.each([false, true])(
|
|
|
308
312
|
|
|
309
313
|
it("opens a startup error even after collapsing preparation and keeps both step outputs available", () => {
|
|
310
314
|
const { store } = mount({ preparing: true, backend: "pixi" });
|
|
311
|
-
const toggle = screen.getByRole("button", { name:
|
|
315
|
+
const toggle = screen.getByRole("button", { name: /^pixi sandbox, / });
|
|
312
316
|
fireEvent.click(toggle);
|
|
313
317
|
act(() => {
|
|
314
318
|
store.set(startupProgressAtom, {
|
|
@@ -318,13 +322,14 @@ it("opens a startup error even after collapsing preparation and keeps both step
|
|
|
318
322
|
});
|
|
319
323
|
store.set(kernelStartupErrorAtom, "Could not import the runtime");
|
|
320
324
|
store.set(connectionAtom, {
|
|
321
|
-
state:
|
|
325
|
+
state: ConnectionState.CLOSED,
|
|
322
326
|
phase: "starting-kernel",
|
|
323
327
|
code: WebSocketClosedReason.KERNEL_STARTUP_ERROR,
|
|
324
328
|
reason: "Kernel startup failed",
|
|
325
329
|
});
|
|
326
330
|
});
|
|
327
331
|
expect(toggle).toHaveAttribute("aria-expanded", "true");
|
|
332
|
+
expect(toggle).toHaveTextContent("pixi sandbox");
|
|
328
333
|
expect(within(toggle).getByRole("status")).toHaveAccessibleName(
|
|
329
334
|
"Kernel failed to start",
|
|
330
335
|
);
|
|
@@ -346,13 +351,80 @@ it("opens a startup error even after collapsing preparation and keeps both step
|
|
|
346
351
|
expect(screen.getByLabelText("Error details")).toBeVisible();
|
|
347
352
|
});
|
|
348
353
|
|
|
349
|
-
it("
|
|
350
|
-
|
|
354
|
+
it.each(["uv", "pixi"] as const)(
|
|
355
|
+
"shows connection status and errors before the %s startup phase arrives",
|
|
356
|
+
(backend) => {
|
|
357
|
+
const { store } = mount({ backend, preparing: true });
|
|
358
|
+
act(() => store.set(connectionAtom, { state: ConnectionState.CONNECTING }));
|
|
359
|
+
const details = within(
|
|
360
|
+
screen.getByRole("region", { name: "Sandbox details" }),
|
|
361
|
+
);
|
|
362
|
+
expect(details.getByRole("status")).toHaveTextContent("Connecting…");
|
|
363
|
+
expect(details.queryByRole("list")).not.toBeInTheDocument();
|
|
364
|
+
|
|
365
|
+
act(() => {
|
|
366
|
+
store.set(kernelStartupErrorAtom, "Kernel process exited unexpectedly");
|
|
367
|
+
store.set(connectionAtom, {
|
|
368
|
+
state: ConnectionState.CLOSED,
|
|
369
|
+
code: WebSocketClosedReason.KERNEL_STARTUP_ERROR,
|
|
370
|
+
reason: "Kernel startup failed",
|
|
371
|
+
});
|
|
372
|
+
});
|
|
373
|
+
expect(details.getByRole("status")).toHaveTextContent(
|
|
374
|
+
"Kernel failed to start",
|
|
375
|
+
);
|
|
376
|
+
expect(details.getByLabelText("Error details")).toHaveTextContent(
|
|
377
|
+
"Kernel process exited unexpectedly",
|
|
378
|
+
);
|
|
379
|
+
expect(details.queryByRole("list")).not.toBeInTheDocument();
|
|
380
|
+
},
|
|
381
|
+
);
|
|
382
|
+
|
|
383
|
+
it("uses the same disclosure for an existing environment and updates it through connection changes", async () => {
|
|
384
|
+
const { store } = mount({ backend: null });
|
|
351
385
|
await screen.findByText("numpy");
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
).
|
|
355
|
-
expect(
|
|
356
|
-
|
|
357
|
-
)
|
|
386
|
+
const toggle = screen.getByRole("button", { name: /^Runtime details, / });
|
|
387
|
+
expect(toggle).toHaveTextContent(/^Runtime$/);
|
|
388
|
+
expect(toggle).toHaveAccessibleName("Runtime details, Kernel ready");
|
|
389
|
+
expect(within(toggle).getByRole("status")).toHaveAccessibleName(
|
|
390
|
+
"Kernel ready",
|
|
391
|
+
);
|
|
392
|
+
expect(toggle).toHaveAttribute("aria-expanded", "false");
|
|
393
|
+
fireEvent.click(toggle);
|
|
394
|
+
const details = screen.getByRole("region", { name: "Runtime details" });
|
|
395
|
+
expect(toggle).toHaveAttribute("aria-controls", details.id);
|
|
396
|
+
expect(within(details).getByText("Kernel started")).toBeVisible();
|
|
397
|
+
expect(within(details).getAllByRole("listitem")).toHaveLength(1);
|
|
398
|
+
expect(within(details).queryByRole("button", { name: "Sync" })).toBeNull();
|
|
399
|
+
|
|
400
|
+
act(() => store.set(connectionAtom, { state: ConnectionState.CONNECTING }));
|
|
401
|
+
expect(toggle).toHaveTextContent(/^Runtime$/);
|
|
402
|
+
expect(within(toggle).getByRole("status")).toHaveAccessibleName(
|
|
403
|
+
"Connecting…",
|
|
404
|
+
);
|
|
405
|
+
expect(within(details).getByText("Starting kernel")).toBeVisible();
|
|
406
|
+
fireEvent.click(toggle);
|
|
407
|
+
expect(details).not.toBeVisible();
|
|
408
|
+
act(() =>
|
|
409
|
+
store.set(connectionAtom, {
|
|
410
|
+
state: ConnectionState.CLOSED,
|
|
411
|
+
code: WebSocketClosedReason.KERNEL_STARTUP_ERROR,
|
|
412
|
+
reason: "Kernel startup failed",
|
|
413
|
+
}),
|
|
414
|
+
);
|
|
415
|
+
expect(toggle).toHaveAttribute("aria-expanded", "true");
|
|
416
|
+
expect(toggle).toHaveTextContent(/^Runtime$/);
|
|
417
|
+
expect(within(toggle).getByRole("status")).toHaveAccessibleName(
|
|
418
|
+
"Kernel failed to start",
|
|
419
|
+
);
|
|
420
|
+
expect(details).toBeVisible();
|
|
421
|
+
expect(toggle).toHaveAccessibleName(
|
|
422
|
+
"Runtime details, Kernel failed to start",
|
|
423
|
+
);
|
|
424
|
+
|
|
425
|
+
act(() => store.set(connectionAtom, { state: ConnectionState.NOT_STARTED }));
|
|
426
|
+
expect(toggle).toHaveTextContent(/^Runtime$/);
|
|
427
|
+
expect(within(toggle).getByRole("status")).toHaveAccessibleName(
|
|
428
|
+
"Kernel not connected",
|
|
429
|
+
);
|
|
358
430
|
});
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
} from "@/core/packages/sandbox-state";
|
|
25
25
|
import { filenameAtom } from "@/core/saving/file-state";
|
|
26
26
|
import { store } from "@/core/state/jotai";
|
|
27
|
-
import { WebSocketClosedReason,
|
|
27
|
+
import { WebSocketClosedReason, ConnectionState } from "@/core/websocket/types";
|
|
28
28
|
import { chromeAtom } from "../../state";
|
|
29
29
|
import PackagesPanel from "../packages-panel";
|
|
30
30
|
import { PanelSectionProvider } from "../panel-context";
|
|
@@ -52,7 +52,7 @@ vi.mock("@/plugins/impl/code/LazyAnyLanguageCodeMirror", () => ({
|
|
|
52
52
|
const manifest =
|
|
53
53
|
'dependencies = ["numpy==0.0.0"]\n\n[tool.custom]\nlabel = "experiment"\n';
|
|
54
54
|
const failed = {
|
|
55
|
-
state:
|
|
55
|
+
state: ConnectionState.CLOSED,
|
|
56
56
|
code: WebSocketClosedReason.KERNEL_STARTUP_ERROR,
|
|
57
57
|
phase: "preparing-environment",
|
|
58
58
|
reason: "Kernel startup failed",
|
|
@@ -117,7 +117,7 @@ it("keeps a whole-manifest repair open through failure and closes only after syn
|
|
|
117
117
|
const reconnect = vi.fn(async () => {
|
|
118
118
|
store.set(kernelStartupErrorAtom, null);
|
|
119
119
|
store.set(connectionAtom, {
|
|
120
|
-
state:
|
|
120
|
+
state: ConnectionState.CONNECTING,
|
|
121
121
|
phase: "preparing-environment",
|
|
122
122
|
});
|
|
123
123
|
});
|
|
@@ -161,7 +161,7 @@ it("keeps a whole-manifest repair open through failure and closes only after syn
|
|
|
161
161
|
);
|
|
162
162
|
fireEvent.click(dialog.getByRole("button", { name: "Sync" }));
|
|
163
163
|
await waitFor(() => expect(reconnect).toHaveBeenCalledTimes(2));
|
|
164
|
-
act(() => store.set(connectionAtom, { state:
|
|
164
|
+
act(() => store.set(connectionAtom, { state: ConnectionState.OPEN }));
|
|
165
165
|
await waitFor(() =>
|
|
166
166
|
expect(screen.queryByRole("dialog")).not.toBeInTheDocument(),
|
|
167
167
|
);
|
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
HelpCircleIcon,
|
|
8
8
|
} from "lucide-react";
|
|
9
9
|
import React from "react";
|
|
10
|
-
import { useConnectionNotice } from "@/core/network/useConnectionNotice";
|
|
11
10
|
import { useOpenSettingsToTab } from "@/components/app-config/state";
|
|
12
11
|
import { Spinner } from "@/components/icons/spinner";
|
|
13
12
|
import { SearchInput } from "@/components/ui/input";
|
|
@@ -26,7 +25,7 @@ import type {
|
|
|
26
25
|
DependencyTreeNode,
|
|
27
26
|
DependencyTreeResponse,
|
|
28
27
|
} from "@/core/network/types";
|
|
29
|
-
import {
|
|
28
|
+
import { sandboxSyncAtom } from "@/core/packages/sandbox-state";
|
|
30
29
|
import { stripPackageManagerPrefix } from "@/core/packages/package-input-utils";
|
|
31
30
|
import { usePackageAction } from "@/core/packages/usePackageAction";
|
|
32
31
|
import { usePackageDependencies } from "@/core/packages/usePackageDependencies";
|
|
@@ -36,7 +35,7 @@ import { ErrorBanner } from "@/plugins/impl/common/error-banner";
|
|
|
36
35
|
import { cn } from "@/utils/cn";
|
|
37
36
|
import { copyToClipboard } from "@/utils/copy";
|
|
38
37
|
import { Events } from "@/utils/events";
|
|
39
|
-
import {
|
|
38
|
+
import { RuntimeDetails } from "./runtime-panel";
|
|
40
39
|
import { PanelEmptyState } from "./empty-state";
|
|
41
40
|
import { PACKAGES_INPUT_ID, packagesToInstallAtom } from "./packages-utils";
|
|
42
41
|
|
|
@@ -69,33 +68,13 @@ const PackageActionButton: React.FC<{
|
|
|
69
68
|
};
|
|
70
69
|
|
|
71
70
|
const PackagesPanel: React.FC = () => {
|
|
72
|
-
const sandbox = useAtomValue(sandboxAtom);
|
|
73
71
|
const connected = useAtomValue(isConnectedAtom);
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
</div>
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
if (notice) {
|
|
84
|
-
return (
|
|
85
|
-
<div className="p-4 text-sm text-muted-foreground">
|
|
86
|
-
<div className="flex items-center gap-2">
|
|
87
|
-
{notice.pending && <Spinner className="size-3" />}
|
|
88
|
-
{notice.title}
|
|
89
|
-
</div>
|
|
90
|
-
{notice.error && (
|
|
91
|
-
<pre className="mt-3 text-xs whitespace-pre-wrap break-words max-h-52 overflow-auto">
|
|
92
|
-
{notice.error}
|
|
93
|
-
</pre>
|
|
94
|
-
)}
|
|
95
|
-
</div>
|
|
96
|
-
);
|
|
97
|
-
}
|
|
98
|
-
return <PackageContents />;
|
|
72
|
+
return (
|
|
73
|
+
<div className="flex flex-col flex-1 min-h-0 overflow-hidden">
|
|
74
|
+
<RuntimeDetails />
|
|
75
|
+
{connected && <PackageContents />}
|
|
76
|
+
</div>
|
|
77
|
+
);
|
|
99
78
|
};
|
|
100
79
|
|
|
101
80
|
const PackageContents: React.FC = () => {
|
package/src/components/editor/chrome/panels/{sandbox-details-state.ts → runtime-details-state.ts}
RENAMED
|
@@ -17,7 +17,7 @@ const disclosureContextAtom = atom((get) => {
|
|
|
17
17
|
};
|
|
18
18
|
});
|
|
19
19
|
|
|
20
|
-
export const
|
|
20
|
+
export const runtimeDetailsExpandedAtom = atom(
|
|
21
21
|
(get) => {
|
|
22
22
|
const context = get(disclosureContextAtom);
|
|
23
23
|
const preference = get(preferenceAtom);
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
import { useAtom, useAtomValue } from "jotai";
|
|
3
3
|
import { RefreshCwIcon } from "lucide-react";
|
|
4
|
-
import { connectionNoticeAtom } from "@/core/network/connection-notice";
|
|
5
4
|
import { Button } from "@/components/ui/button";
|
|
6
5
|
import { useRestartKernel } from "@/components/editor/actions/useRestartKernel";
|
|
7
6
|
import {
|
|
8
7
|
isConnectedAtom,
|
|
9
8
|
startupProgressAtom,
|
|
10
9
|
} from "@/core/network/connection";
|
|
10
|
+
import { connectionNoticeAtom } from "@/core/network/connection-notice";
|
|
11
11
|
import type { DisplayConnectionNotice } from "@/core/network/useConnectionNotice";
|
|
12
12
|
import {
|
|
13
13
|
preparationAtom,
|
|
@@ -17,25 +17,10 @@ import {
|
|
|
17
17
|
sandboxSyncOperationAtom,
|
|
18
18
|
} from "@/core/packages/sandbox-state";
|
|
19
19
|
import { cn } from "@/utils/cn";
|
|
20
|
-
import { usePanelSection } from "./panel-context";
|
|
21
|
-
import { sandboxDetailsExpandedAtom } from "./sandbox-details-state";
|
|
22
20
|
import { StartupOutput } from "../../alerts/startup-output";
|
|
23
|
-
import {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
} from "../../alerts/startup-progress";
|
|
27
|
-
|
|
28
|
-
export function SandboxErrorOutput({ error }: { error: string }) {
|
|
29
|
-
return (
|
|
30
|
-
<pre
|
|
31
|
-
tabIndex={0}
|
|
32
|
-
aria-label="Error details"
|
|
33
|
-
className="mt-3 p-3 rounded border bg-muted/30 text-xs text-muted-foreground whitespace-pre min-w-0 max-w-full max-h-52 overflow-auto"
|
|
34
|
-
>
|
|
35
|
-
{error}
|
|
36
|
-
</pre>
|
|
37
|
-
);
|
|
38
|
-
}
|
|
21
|
+
import { ConnectionDetails } from "../../alerts/connection-details";
|
|
22
|
+
import { usePanelSection } from "./panel-context";
|
|
23
|
+
import { runtimeDetailsExpandedAtom } from "./runtime-details-state";
|
|
39
24
|
|
|
40
25
|
function SandboxRecovery({ notice }: { notice: DisplayConnectionNotice }) {
|
|
41
26
|
const sandbox = useAtomValue(sandboxAtom);
|
|
@@ -46,8 +31,6 @@ function SandboxRecovery({ notice }: { notice: DisplayConnectionNotice }) {
|
|
|
46
31
|
}
|
|
47
32
|
return (
|
|
48
33
|
<>
|
|
49
|
-
<p className="mt-2 text-foreground">{notice.description}</p>
|
|
50
|
-
{notice.error && <SandboxErrorOutput error={notice.error} />}
|
|
51
34
|
<div className="flex items-center gap-2 mt-3">
|
|
52
35
|
<Button
|
|
53
36
|
variant="outline"
|
|
@@ -88,36 +71,11 @@ function SandboxRestart() {
|
|
|
88
71
|
);
|
|
89
72
|
}
|
|
90
73
|
|
|
91
|
-
function
|
|
92
|
-
const showSteps =
|
|
93
|
-
notice.kind === "startup" &&
|
|
94
|
-
(notice.ready ||
|
|
95
|
-
notice.phase === "preparing-environment" ||
|
|
96
|
-
notice.phase === "starting-kernel");
|
|
97
|
-
return (
|
|
98
|
-
<div className="min-w-0 text-sm">
|
|
99
|
-
{showSteps ? (
|
|
100
|
-
<StartupProgress notice={notice} surface="sidebar" />
|
|
101
|
-
) : (
|
|
102
|
-
<div className="flex items-center gap-2" role="status">
|
|
103
|
-
<ConnectionStatusIcon notice={notice} />
|
|
104
|
-
<h2>{notice.title}</h2>
|
|
105
|
-
</div>
|
|
106
|
-
)}
|
|
107
|
-
<SandboxRecovery notice={notice} />
|
|
108
|
-
</div>
|
|
109
|
-
);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function SandboxSyncStatus({ notice }: { notice: DisplayConnectionNotice }) {
|
|
74
|
+
function RuntimeNoticeDetails({ notice }: { notice: DisplayConnectionNotice }) {
|
|
113
75
|
return (
|
|
114
|
-
<
|
|
115
|
-
<
|
|
116
|
-
|
|
117
|
-
<span>{notice.title}</span>
|
|
118
|
-
</output>
|
|
119
|
-
<SandboxRecovery notice={notice} />
|
|
120
|
-
</div>
|
|
76
|
+
<ConnectionDetails notice={notice}>
|
|
77
|
+
{notice.sandbox && <SandboxRecovery notice={notice} />}
|
|
78
|
+
</ConnectionDetails>
|
|
121
79
|
);
|
|
122
80
|
}
|
|
123
81
|
|
|
@@ -133,7 +91,9 @@ function SandboxSyncDetails({
|
|
|
133
91
|
return (
|
|
134
92
|
<div className="mt-5 min-w-0">
|
|
135
93
|
{notice ? (
|
|
136
|
-
<
|
|
94
|
+
<div className="mb-5">
|
|
95
|
+
<RuntimeNoticeDetails notice={notice} />
|
|
96
|
+
</div>
|
|
137
97
|
) : (
|
|
138
98
|
<p className="text-sm">Environment synced</p>
|
|
139
99
|
)}
|
|
@@ -153,20 +113,25 @@ function SandboxSyncDetails({
|
|
|
153
113
|
);
|
|
154
114
|
}
|
|
155
115
|
|
|
156
|
-
export function
|
|
116
|
+
export function RuntimeDetails() {
|
|
157
117
|
const section = usePanelSection();
|
|
118
|
+
const sandbox = Boolean(useAtomValue(sandboxAtom)?.backend);
|
|
158
119
|
const connected = useAtomValue(isConnectedAtom);
|
|
159
|
-
const
|
|
120
|
+
const connectionNotice = useAtomValue(connectionNoticeAtom);
|
|
121
|
+
const notice: DisplayConnectionNotice | null = connectionNotice && {
|
|
122
|
+
...connectionNotice,
|
|
123
|
+
ready: false,
|
|
124
|
+
};
|
|
160
125
|
const preparation = useAtomValue(preparationAtom);
|
|
161
126
|
const progress = useAtomValue(startupProgressAtom);
|
|
162
|
-
const [expanded, setExpanded] = useAtom(
|
|
127
|
+
const [expanded, setExpanded] = useAtom(runtimeDetailsExpandedAtom);
|
|
163
128
|
const startup: DisplayConnectionNotice | null =
|
|
164
129
|
notice?.kind === "startup"
|
|
165
|
-
?
|
|
166
|
-
: preparation || progress
|
|
130
|
+
? notice
|
|
131
|
+
: preparation || progress || (connected && !sandbox)
|
|
167
132
|
? {
|
|
168
133
|
kind: "startup",
|
|
169
|
-
sandbox
|
|
134
|
+
sandbox,
|
|
170
135
|
title: "Notebook started",
|
|
171
136
|
description: "The notebook is ready to run.",
|
|
172
137
|
pending: false,
|
|
@@ -176,8 +141,8 @@ export function SandboxDetails() {
|
|
|
176
141
|
: null;
|
|
177
142
|
return (
|
|
178
143
|
<section
|
|
179
|
-
id={`
|
|
180
|
-
aria-label="Sandbox details"
|
|
144
|
+
id={`runtime-details-${section}`}
|
|
145
|
+
aria-label={sandbox ? "Sandbox details" : "Runtime details"}
|
|
181
146
|
hidden={!expanded}
|
|
182
147
|
className={cn(
|
|
183
148
|
"min-w-0 overflow-auto p-4",
|
|
@@ -187,15 +152,22 @@ export function SandboxDetails() {
|
|
|
187
152
|
onFocusCapture={() => setExpanded(true)}
|
|
188
153
|
>
|
|
189
154
|
{notice?.kind === "connection" && (
|
|
190
|
-
<
|
|
155
|
+
<div className="mb-5">
|
|
156
|
+
<RuntimeNoticeDetails notice={notice} />
|
|
157
|
+
</div>
|
|
191
158
|
)}
|
|
192
|
-
{startup && <
|
|
193
|
-
{connected && (
|
|
194
|
-
<SandboxSyncDetails
|
|
195
|
-
|
|
196
|
-
|
|
159
|
+
{startup && <RuntimeNoticeDetails notice={startup} />}
|
|
160
|
+
{sandbox && connected && (
|
|
161
|
+
<SandboxSyncDetails notice={notice?.kind === "sync" ? notice : null} />
|
|
162
|
+
)}
|
|
163
|
+
{sandbox && connected && (!notice || notice.pending) && (
|
|
164
|
+
<SandboxActions />
|
|
165
|
+
)}
|
|
166
|
+
{!notice && !startup && !sandbox && (
|
|
167
|
+
<p className="text-sm text-muted-foreground">
|
|
168
|
+
Not connected to a runtime.
|
|
169
|
+
</p>
|
|
197
170
|
)}
|
|
198
|
-
{connected && (!notice || notice.pending) && <SandboxActions />}
|
|
199
171
|
</section>
|
|
200
172
|
);
|
|
201
173
|
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
import { useAtom, useAtomValue } from "jotai";
|
|
3
|
+
import { ChevronDownIcon } from "lucide-react";
|
|
4
|
+
import {
|
|
5
|
+
ProgressStatusIcon,
|
|
6
|
+
type ProgressState,
|
|
7
|
+
} from "@/components/ui/progress-stages";
|
|
8
|
+
import { isConnectedAtom } from "@/core/network/connection";
|
|
9
|
+
import { connectionNoticeAtom } from "@/core/network/connection-notice";
|
|
10
|
+
import { sandboxAtom } from "@/core/packages/sandbox-state";
|
|
11
|
+
import { cn } from "@/utils/cn";
|
|
12
|
+
import type { PanelSection } from "./panel-context";
|
|
13
|
+
import { runtimeDetailsExpandedAtom } from "./runtime-details-state";
|
|
14
|
+
|
|
15
|
+
export function RuntimeStatusToggle({ section }: { section: PanelSection }) {
|
|
16
|
+
const sandbox = useAtomValue(sandboxAtom);
|
|
17
|
+
const notice = useAtomValue(connectionNoticeAtom);
|
|
18
|
+
const [expanded, setExpanded] = useAtom(runtimeDetailsExpandedAtom);
|
|
19
|
+
const connected = useAtomValue(isConnectedAtom);
|
|
20
|
+
const label = sandbox?.backend ? `${sandbox.backend} sandbox` : "Runtime";
|
|
21
|
+
const statusLabel =
|
|
22
|
+
notice?.title ?? (connected ? "Kernel ready" : "Kernel not connected");
|
|
23
|
+
const status: ProgressState = notice
|
|
24
|
+
? notice.pending
|
|
25
|
+
? "running"
|
|
26
|
+
: "failed"
|
|
27
|
+
: connected
|
|
28
|
+
? "succeeded"
|
|
29
|
+
: "waiting";
|
|
30
|
+
return (
|
|
31
|
+
<button
|
|
32
|
+
type="button"
|
|
33
|
+
aria-label={`${sandbox?.backend ? label : "Runtime details"}, ${statusLabel}`}
|
|
34
|
+
aria-expanded={expanded}
|
|
35
|
+
aria-controls={`runtime-details-${section}`}
|
|
36
|
+
title={statusLabel}
|
|
37
|
+
className={cn(
|
|
38
|
+
"flex min-w-0 items-center gap-1.5 rounded px-1.5 py-1 text-xs whitespace-nowrap hover:bg-accent focus-visible:outline-2 focus-visible:outline-ring",
|
|
39
|
+
status === "failed" ? "text-(--red-11)" : "text-foreground",
|
|
40
|
+
)}
|
|
41
|
+
onClick={() => setExpanded(!expanded)}
|
|
42
|
+
>
|
|
43
|
+
<ProgressStatusIcon state={status} variant="dot" />
|
|
44
|
+
<output aria-label={statusLabel} className="truncate">
|
|
45
|
+
{label}
|
|
46
|
+
</output>
|
|
47
|
+
<ChevronDownIcon
|
|
48
|
+
aria-hidden={true}
|
|
49
|
+
className={cn(
|
|
50
|
+
"size-3 shrink-0 text-muted-foreground",
|
|
51
|
+
expanded && "rotate-180",
|
|
52
|
+
)}
|
|
53
|
+
/>
|
|
54
|
+
</button>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
@@ -14,7 +14,7 @@ import { useSandboxController } from "@/core/packages/useSandboxController";
|
|
|
14
14
|
import { LazyAnyLanguageCodeMirror } from "@/plugins/impl/code/LazyAnyLanguageCodeMirror";
|
|
15
15
|
import { useTheme } from "@/theme/useTheme";
|
|
16
16
|
import { Paths } from "@/utils/paths";
|
|
17
|
-
import {
|
|
17
|
+
import { ConnectionErrorOutput } from "../../alerts/connection-details";
|
|
18
18
|
|
|
19
19
|
export function SandboxController({
|
|
20
20
|
onReconnect,
|
|
@@ -64,7 +64,7 @@ export function SandboxController({
|
|
|
64
64
|
) : (
|
|
65
65
|
!error && <Spinner />
|
|
66
66
|
)}
|
|
67
|
-
{diagnostic && <
|
|
67
|
+
{diagnostic && <ConnectionErrorOutput error={diagnostic} />}
|
|
68
68
|
{conflict && (
|
|
69
69
|
<Button variant="text" size="sm" onClick={loadManifest}>
|
|
70
70
|
Discard draft and reload manifest
|
|
@@ -34,7 +34,7 @@ import { ErrorBoundary } from "../../boundary/ErrorBoundary";
|
|
|
34
34
|
import { raf2 } from "../../navigation/focus-utils";
|
|
35
35
|
import { ContextAwarePanel } from "../panels/context-aware-panel/context-aware-panel";
|
|
36
36
|
import { PanelSectionProvider } from "../panels/panel-context";
|
|
37
|
-
import {
|
|
37
|
+
import { RuntimeStatusToggle } from "../panels/runtime-status-toggle";
|
|
38
38
|
import { useTheme } from "@/theme/useTheme";
|
|
39
39
|
import {
|
|
40
40
|
LazyAgentPanel,
|
|
@@ -391,7 +391,7 @@ export const AppChrome: React.FC<PropsWithChildren> = ({ children }) => {
|
|
|
391
391
|
</span>
|
|
392
392
|
)}
|
|
393
393
|
{selectedPanel === "packages" && (
|
|
394
|
-
<
|
|
394
|
+
<RuntimeStatusToggle section="sidebar" />
|
|
395
395
|
)}
|
|
396
396
|
<Button
|
|
397
397
|
data-testid="close-helper-pane"
|
|
@@ -549,7 +549,7 @@ export const AppChrome: React.FC<PropsWithChildren> = ({ children }) => {
|
|
|
549
549
|
<LspStatus />
|
|
550
550
|
<div className="flex-1" />
|
|
551
551
|
{selectedDeveloperPanelTab === "packages" && (
|
|
552
|
-
<
|
|
552
|
+
<RuntimeStatusToggle section="developer-panel" />
|
|
553
553
|
)}
|
|
554
554
|
<Button
|
|
555
555
|
size="xs"
|