@marimo-team/islands 0.24.3-dev81 → 0.24.3-dev83

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 (47) hide show
  1. package/dist/{ErrorBoundary-CCi-VkF7.js → ErrorBoundary-Uf_LvjvY.js} +1 -1
  2. package/dist/{cells-BIfOnGBK.js → cells-DYIkSxs6.js} +4 -0
  3. package/dist/{chat-ui-LWkjhObe.js → chat-ui-BXj3uYjy.js} +4 -4
  4. package/dist/{code-visibility-Bsfqbe2z.js → code-visibility-DxgQWu1j.js} +2 -2
  5. package/dist/{common-BnKC1tWB.js → common-ByAtncLr.js} +3 -3
  6. package/dist/{data-grid-overlay-editor-CJnlWykO.js → data-grid-overlay-editor-DL_blFyL.js} +1 -1
  7. package/dist/{error-entries-CR0oClSL.js → error-entries-C0NsUqXl.js} +1 -1
  8. package/dist/{feedback-button-MWISfIIf.js → feedback-button-AZcBqfoV.js} +4 -4
  9. package/dist/{glide-data-editor-dcmP8mKS.js → glide-data-editor-BJdZncJk.js} +3 -3
  10. package/dist/{globals-fCMBgbAD.js → globals-yWarpyN4.js} +1 -1
  11. package/dist/{html-to-image-_i03yR4q.js → html-to-image-CeYYvQNb.js} +1 -1
  12. package/dist/main.js +12 -11
  13. package/dist/{number-overlay-editor-DhNE6pBe.js → number-overlay-editor-BatBVYxr.js} +1 -1
  14. package/dist/{process-output-D3zQyTAS.js → process-output-BCTjlyuH.js} +2 -2
  15. package/dist/{reveal-component-Cdcgngkc.js → reveal-component-D02jcpiJ.js} +3 -3
  16. package/dist/style.css +1 -1
  17. package/package.json +1 -1
  18. package/src/components/editor/alerts/__tests__/connection-notice.test.tsx +186 -0
  19. package/src/components/editor/alerts/__tests__/startup-progress.test.tsx +185 -0
  20. package/src/components/editor/alerts/connection-notice.tsx +100 -54
  21. package/src/components/editor/alerts/startup-output.tsx +173 -0
  22. package/src/components/editor/alerts/startup-progress.tsx +204 -0
  23. package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +53 -4
  24. package/src/components/editor/chrome/panels/__tests__/sandbox-details.test.tsx +358 -0
  25. package/src/components/editor/chrome/panels/__tests__/sandbox-recovery.test.tsx +5 -2
  26. package/src/components/editor/chrome/panels/packages-panel.tsx +32 -21
  27. package/src/components/editor/chrome/panels/sandbox-details-state.ts +38 -0
  28. package/src/components/editor/chrome/panels/sandbox-panel.tsx +188 -131
  29. package/src/components/editor/chrome/panels/sandbox-toggle.tsx +52 -0
  30. package/src/components/editor/chrome/wrapper/app-chrome.tsx +7 -0
  31. package/src/components/editor/package-alert.tsx +112 -66
  32. package/src/core/alerts/__tests__/environment.test.ts +183 -0
  33. package/src/core/alerts/__tests__/state.test.tsx +240 -0
  34. package/src/core/alerts/environment.ts +57 -0
  35. package/src/core/alerts/state.ts +155 -99
  36. package/src/core/islands/bootstrap.ts +2 -1
  37. package/src/core/kernel/messages.ts +2 -2
  38. package/src/core/network/connection-notice.ts +45 -12
  39. package/src/core/network/connection.ts +19 -0
  40. package/src/core/network/useConnectionNotice.ts +53 -0
  41. package/src/core/packages/__tests__/useSandboxController.test.tsx +86 -5
  42. package/src/core/packages/sandbox-state.ts +56 -4
  43. package/src/core/packages/useSandboxController.ts +27 -7
  44. package/src/core/websocket/__tests__/useMarimoKernelConnection.hook.test.tsx +199 -8
  45. package/src/core/websocket/types.ts +1 -1
  46. package/src/core/websocket/useMarimoKernelConnection.tsx +31 -19
  47. package/src/stories/sandbox.stories.tsx +230 -50
@@ -3,14 +3,15 @@ import type { Meta, StoryObj } from "@storybook/react-vite";
3
3
  import { createStore, Provider, useAtomValue } from "jotai";
4
4
  import { useEffect, useRef, useState } from "react";
5
5
  import { ConnectionNotice } from "@/components/editor/alerts/connection-notice";
6
- import {
7
- SandboxFooter,
8
- SandboxStartupPanel,
9
- } from "@/components/editor/chrome/panels/sandbox-panel";
6
+ import PackagesPanel from "@/components/editor/chrome/panels/packages-panel";
7
+ import { PanelSectionProvider } from "@/components/editor/chrome/panels/panel-context";
8
+ import { SandboxToggle } from "@/components/editor/chrome/panels/sandbox-toggle";
10
9
  import { SandboxController } from "@/components/editor/chrome/panels/sandbox-controller";
11
10
  import { chromeAtom } from "@/components/editor/chrome/state";
12
11
  import { Cell } from "@/components/editor/notebook-cell";
13
12
  import { VerticalLayoutWrapper } from "@/components/editor/renderers/vertical-layout/vertical-layout-wrapper";
13
+ import { alertAtom } from "@/core/alerts/state";
14
+ import type { EnvironmentOperation } from "@/core/alerts/environment";
14
15
  import {
15
16
  createNotebookActions,
16
17
  notebookAtom,
@@ -18,18 +19,15 @@ import {
18
19
  } from "@/core/cells/cells";
19
20
  import { userConfigAtom } from "@/core/config/config";
20
21
  import {
22
+ type AppConfig,
21
23
  AppConfigSchema,
22
24
  defaultUserConfig,
23
25
  } from "@/core/config/config-schema";
24
26
  import { kernelStartupErrorAtom } from "@/core/errors/state";
25
- import { connectionAtom } from "@/core/network/connection";
27
+ import { connectionAtom, startupProgressAtom } from "@/core/network/connection";
26
28
  import { requestClientAtom } from "@/core/network/requests";
27
29
  import { createStaticRequests } from "@/core/network/requests-static";
28
- import {
29
- sandboxActionsAtom,
30
- sandboxAtom,
31
- sandboxSyncAtom,
32
- } from "@/core/packages/sandbox-state";
30
+ import { sandboxActionsAtom, sandboxAtom } from "@/core/packages/sandbox-state";
33
31
  import { filenameAtom } from "@/core/saving/file-state";
34
32
  import { WebSocketClosedReason, WebSocketState } from "@/core/websocket/types";
35
33
  import { HTTPError } from "@/utils/errors";
@@ -46,6 +44,19 @@ description = "Explore bike trips"
46
44
  const resolutionError = `No solution found when resolving dependencies:
47
45
  Because there is no version of rich==999.0.0 and your script depends
48
46
  on rich==999.0.0, your script's requirements are unsatisfiable.`;
47
+ const preparationOutput = `Using Python 3.13
48
+ Resolving notebook dependencies
49
+ Resolved 24 packages
50
+ Downloading polars
51
+ Downloading rich
52
+ Downloading marimo
53
+ Downloading pyzmq
54
+ Downloaded rich
55
+ Downloaded polars
56
+ Downloaded marimo
57
+ Preparing packages
58
+ Installing packages
59
+ `;
49
60
 
50
61
  interface Props {
51
62
  surface: "notebook" | "packages" | "manifest";
@@ -55,11 +66,68 @@ interface Props {
55
66
  | "failed"
56
67
  | "ready"
57
68
  | "syncing"
58
- | "sync-failed";
69
+ | "sync-failed"
70
+ | "synced"
71
+ | "restart-required";
59
72
  existingCells: boolean;
60
73
  backend: "uv" | "pixi";
61
74
  saveResult: "success" | "failure" | "stale";
62
75
  theme: "light" | "dark";
76
+ interactive: boolean;
77
+ }
78
+
79
+ function SandboxPreview({
80
+ props,
81
+ appConfig,
82
+ reconnect,
83
+ }: {
84
+ props: Props;
85
+ appConfig: AppConfig;
86
+ reconnect: () => Promise<void>;
87
+ }) {
88
+ const chrome = useAtomValue(chromeAtom);
89
+ const notebook = useAtomValue(notebookAtom);
90
+ const userConfig = useAtomValue(userConfigAtom);
91
+ const showPackages = props.surface === "packages" || chrome.isSidebarOpen;
92
+ return (
93
+ <div
94
+ className="flex bg-background text-foreground border rounded min-h-[460px]"
95
+ style={{ width: props.surface === "notebook" ? 1000 : 300 }}
96
+ data-testid="sandbox-story"
97
+ >
98
+ {showPackages && props.surface !== "manifest" && (
99
+ <aside className="flex flex-col w-[300px] shrink-0 border-r h-[520px]">
100
+ <div className="flex items-center justify-between px-3 py-2 border-b text-sm">
101
+ Packages
102
+ <SandboxToggle section="sidebar" />
103
+ </div>
104
+ <PanelSectionProvider value="sidebar">
105
+ <PackagesPanel />
106
+ </PanelSectionProvider>
107
+ </aside>
108
+ )}
109
+ {props.surface === "notebook" && (
110
+ <div className="flex-1 min-w-0 pt-12">
111
+ <ConnectionNotice appConfig={appConfig} onRetry={reconnect} />
112
+ {notebook.cellIds.inOrderIds.map((cellId) => (
113
+ <VerticalLayoutWrapper key={cellId} appConfig={appConfig}>
114
+ <Cell
115
+ cellId={cellId}
116
+ theme={props.theme}
117
+ mode="edit"
118
+ showPlaceholder={false}
119
+ canDelete={true}
120
+ isCollapsed={false}
121
+ collapseCount={0}
122
+ canMoveX={false}
123
+ userConfig={userConfig}
124
+ />
125
+ </VerticalLayoutWrapper>
126
+ ))}
127
+ </div>
128
+ )}
129
+ </div>
130
+ );
63
131
  }
64
132
 
65
133
  function OpenManifest() {
@@ -121,9 +189,74 @@ function SandboxStory(props: Props) {
121
189
  kernelStartupErrorAtom,
122
190
  props.phase === "failed" ? resolutionError : null,
123
191
  );
124
- state.set(sandboxSyncAtom, {
125
- pending: props.phase === "syncing",
126
- error: props.phase === "sync-failed" ? resolutionError : null,
192
+ const syncStatus: EnvironmentOperation["status"] | null =
193
+ props.phase === "syncing"
194
+ ? { kind: "running" }
195
+ : props.phase === "sync-failed"
196
+ ? { kind: "failed", error: resolutionError }
197
+ : props.phase === "restart-required"
198
+ ? {
199
+ kind: "restart-required",
200
+ reason:
201
+ "The manifest requires Python 3.14. This kernel uses Python 3.13.",
202
+ }
203
+ : props.phase === "synced"
204
+ ? { kind: "succeeded" }
205
+ : null;
206
+ state.set(alertAtom, (value) => ({
207
+ ...value,
208
+ environments: {
209
+ ...value.environments,
210
+ kernel: {
211
+ 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
+ },
243
+ },
244
+ ]
245
+ : []),
246
+ ],
247
+ },
248
+ },
249
+ }));
250
+ 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
+ : "",
259
+ log_mode: "replace",
127
260
  });
128
261
  if (props.existingCells) {
129
262
  const actions = createNotebookActions((action) =>
@@ -143,6 +276,18 @@ function SandboxStory(props: Props) {
143
276
  manifest: currentManifest,
144
277
  filename: "bike_trips.py",
145
278
  }),
279
+ getDependencyTree: async () => ({
280
+ context: { kind: "sandbox", backend: props.backend },
281
+ tree: {
282
+ name: "<root>",
283
+ version: null,
284
+ tags: [],
285
+ dependencies: [
286
+ { name: "marimo", version: "0.24.2", tags: [], dependencies: [] },
287
+ { name: "polars", version: "1.34.0", tags: [], dependencies: [] },
288
+ ],
289
+ },
290
+ }),
146
291
  updateManifest: async ({ contents }) => {
147
292
  if (props.saveResult === "stale") {
148
293
  throw new HTTPError(
@@ -184,46 +329,56 @@ function SandboxStory(props: Props) {
184
329
  const reconnect = async () => {
185
330
  store.set(connectionAtom, { state: WebSocketState.OPEN });
186
331
  };
332
+ useEffect(() => {
333
+ if (!props.interactive) {
334
+ return;
335
+ }
336
+ const appendPreparation = (line: string) =>
337
+ store.set(alertAtom, (value) => ({
338
+ ...value,
339
+ environments: {
340
+ ...value.environments,
341
+ kernel: {
342
+ ...value.environments.kernel,
343
+ operations: value.environments.kernel.operations.map(
344
+ (operation) => ({
345
+ ...operation,
346
+ logs: { environment: operation.logs.environment + line },
347
+ }),
348
+ ),
349
+ },
350
+ },
351
+ }));
352
+ const timers = [
353
+ setTimeout(() => appendPreparation("Installed 24 packages\n"), 1500),
354
+ setTimeout(() => appendPreparation("Environment prepared\n"), 2500),
355
+ setTimeout(() => {
356
+ store.set(startupProgressAtom, {
357
+ phase: "starting-kernel",
358
+ logs: "Launching kernel process\nWaiting for kernel connection\n",
359
+ log_mode: "replace",
360
+ });
361
+ store.set(connectionAtom, {
362
+ state: WebSocketState.CONNECTING,
363
+ phase: "starting-kernel",
364
+ });
365
+ }, 3500),
366
+ setTimeout(
367
+ () => store.set(connectionAtom, { state: WebSocketState.OPEN }),
368
+ 5500,
369
+ ),
370
+ ];
371
+ return () => timers.forEach(clearTimeout);
372
+ }, [props.interactive, store]);
187
373
  return (
188
374
  <Provider store={store}>
189
375
  <SandboxController onReconnect={reconnect} />
190
376
  {props.surface === "manifest" && <OpenManifest />}
191
- <div
192
- data-testid="sandbox-story"
193
- className="bg-background text-foreground"
194
- style={{
195
- width: props.surface === "notebook" ? 820 : 400,
196
- }}
197
- >
198
- {props.surface === "notebook" && (
199
- <>
200
- <ConnectionNotice appConfig={appConfig} onRetry={reconnect} />
201
- {store.get(notebookAtom).cellIds.inOrderIds.map((cellId) => (
202
- <VerticalLayoutWrapper key={cellId} appConfig={appConfig}>
203
- <Cell
204
- cellId={cellId}
205
- theme={props.theme}
206
- mode="edit"
207
- showPlaceholder={false}
208
- canDelete={true}
209
- isCollapsed={false}
210
- collapseCount={0}
211
- canMoveX={false}
212
- userConfig={store.get(userConfigAtom)}
213
- />
214
- </VerticalLayoutWrapper>
215
- ))}
216
- </>
217
- )}
218
- {props.surface === "packages" &&
219
- (props.phase === "failed" ||
220
- props.phase === "sync-failed" ||
221
- props.phase === "preparing" ? (
222
- <SandboxStartupPanel />
223
- ) : (
224
- <SandboxFooter />
225
- ))}
226
- </div>
377
+ <SandboxPreview
378
+ props={props}
379
+ appConfig={appConfig}
380
+ reconnect={reconnect}
381
+ />
227
382
  </Provider>
228
383
  );
229
384
  }
@@ -239,6 +394,7 @@ const meta = {
239
394
  backend: "uv",
240
395
  saveResult: "success",
241
396
  theme: "light",
397
+ interactive: false,
242
398
  },
243
399
  render: (args, context) => (
244
400
  <SandboxStory
@@ -262,6 +418,14 @@ export const StartingKernel: Story = {
262
418
  name: "Startup notice / starting kernel",
263
419
  args: { phase: "starting" },
264
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
+ };
265
429
  export const ExistingNotebookFailed: Story = {
266
430
  name: "Startup notice / failed",
267
431
  args: { phase: "failed" },
@@ -274,6 +438,14 @@ export const PackagesPreparing: Story = {
274
438
  name: "Setup details / preparing",
275
439
  args: { surface: "packages" },
276
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
+ };
277
449
  export const PackagesFailed: Story = {
278
450
  name: "Setup details / failed",
279
451
  args: { surface: "packages", phase: "failed" },
@@ -290,6 +462,14 @@ export const PackagesSyncFailed: Story = {
290
462
  name: "Setup details / sync failed",
291
463
  args: { surface: "packages", phase: "sync-failed" },
292
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
+ };
293
473
  export const PixiPackagesReady: Story = {
294
474
  name: "Status row / pixi",
295
475
  args: { surface: "packages", phase: "ready", backend: "pixi" },