@marimo-team/islands 0.19.2-dev0 → 0.19.3-dev10

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marimo-team/islands",
3
- "version": "0.19.2-dev0",
3
+ "version": "0.19.3-dev10",
4
4
  "main": "dist/main.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -0,0 +1,101 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { useAtom } from "jotai";
4
+ import { CopyIcon, HomeIcon, XCircleIcon } from "lucide-react";
5
+ import { kernelStartupErrorAtom } from "@/core/errors/state";
6
+ import {
7
+ AlertDialog,
8
+ AlertDialogAction,
9
+ AlertDialogContent,
10
+ AlertDialogDescription,
11
+ AlertDialogFooter,
12
+ AlertDialogHeader,
13
+ AlertDialogTitle,
14
+ } from "../ui/alert-dialog";
15
+ import { Button } from "../ui/button";
16
+ import { toast } from "../ui/use-toast";
17
+
18
+ /**
19
+ * Modal that displays kernel startup errors.
20
+ * Shows when the kernel fails to start in sandbox mode,
21
+ * displaying the stderr output so users can diagnose the issue.
22
+ */
23
+ export const KernelStartupErrorModal: React.FC = () => {
24
+ const [error, setError] = useAtom(kernelStartupErrorAtom);
25
+
26
+ if (error === null) {
27
+ return null;
28
+ }
29
+
30
+ const handleCopy = async () => {
31
+ try {
32
+ await navigator.clipboard.writeText(error);
33
+ toast({
34
+ title: "Copied to clipboard",
35
+ description: "Error details have been copied to your clipboard.",
36
+ });
37
+ } catch {
38
+ toast({
39
+ title: "Failed to copy",
40
+ description: "Could not copy to clipboard.",
41
+ variant: "danger",
42
+ });
43
+ }
44
+ };
45
+
46
+ const handleClose = () => {
47
+ setError(null);
48
+ };
49
+
50
+ const handleReturnHome = () => {
51
+ const withoutSearch = document.baseURI.split("?")[0];
52
+ window.open(withoutSearch, "_self");
53
+ };
54
+
55
+ return (
56
+ <AlertDialog open={true} onOpenChange={(open) => !open && handleClose()}>
57
+ <AlertDialogContent className="max-w-2xl">
58
+ <AlertDialogHeader>
59
+ <AlertDialogTitle className="flex items-center gap-2 text-destructive">
60
+ <XCircleIcon className="h-5 w-5" />
61
+ Kernel Startup Failed
62
+ </AlertDialogTitle>
63
+ <AlertDialogDescription>
64
+ The kernel failed to start. This usually happens when the package
65
+ manager can't install your notebook's dependencies.
66
+ </AlertDialogDescription>
67
+ </AlertDialogHeader>
68
+ <div className="my-4">
69
+ <div className="flex items-center justify-between mb-2">
70
+ <span className="text-sm font-medium text-muted-foreground">
71
+ Error Details
72
+ </span>
73
+ <Button
74
+ variant="outline"
75
+ size="xs"
76
+ onClick={handleCopy}
77
+ className="flex items-center gap-1"
78
+ >
79
+ <CopyIcon className="h-3 w-3" />
80
+ Copy
81
+ </Button>
82
+ </div>
83
+ <pre className="bg-muted p-4 rounded-md text-sm font-mono overflow-auto max-h-80 whitespace-pre-wrap break-words">
84
+ {error}
85
+ </pre>
86
+ </div>
87
+ <AlertDialogFooter>
88
+ <Button
89
+ variant="outline"
90
+ onClick={handleReturnHome}
91
+ className="flex items-center gap-2"
92
+ >
93
+ <HomeIcon className="h-4 w-4" />
94
+ Return to Home
95
+ </Button>
96
+ <AlertDialogAction onClick={handleClose}>Dismiss</AlertDialogAction>
97
+ </AlertDialogFooter>
98
+ </AlertDialogContent>
99
+ </AlertDialog>
100
+ );
101
+ };
@@ -585,7 +585,7 @@ const EditableCellComponent = ({
585
585
  className={cn(
586
586
  className,
587
587
  navigationProps.className,
588
- "focus:ring-1 focus:ring-(--slate-7) focus:ring-offset-2",
588
+ "focus:ring-1 focus:ring-(--slate-8) focus:ring-offset-2",
589
589
  )}
590
590
  ref={cellContainerRef}
591
591
  {...cellDomProps(cellId, cellData.name)}
@@ -1032,16 +1032,12 @@ const SetupCellComponent = ({
1032
1032
  const hasConsoleOutput = cellRuntime.consoleOutputs.length > 0;
1033
1033
  const isErrorOutput = isErrorMime(cellRuntime.output?.mimetype);
1034
1034
 
1035
- const className = clsx(
1036
- "marimo-cell",
1037
- "hover-actions-parent z-10 border shadow-sm",
1038
- "border-(--blue-5)! rounded-sm!",
1039
- {
1040
- "needs-run": needsRun,
1041
- "has-error": cellRuntime.errored,
1042
- stopped: cellRuntime.stopped,
1043
- },
1044
- );
1035
+ const className = clsx("marimo-cell", "hover-actions-parent z-10", {
1036
+ interactive: true,
1037
+ "needs-run": needsRun,
1038
+ "has-error": cellRuntime.errored,
1039
+ stopped: cellRuntime.stopped,
1040
+ });
1045
1041
 
1046
1042
  const handleRefactorWithAI: OnRefactorWithAI = useEvent(
1047
1043
  (opts: { prompt: string; triggerImmediately: boolean }) => {
@@ -1076,7 +1072,7 @@ const SetupCellComponent = ({
1076
1072
  {...mergeProps(navigationProps, {
1077
1073
  className: cn(
1078
1074
  className,
1079
- "focus:ring-1 focus:ring-(--blue-7) focus:ring-offset-0",
1075
+ "focus:ring-1 focus:ring-(--slate-8) focus:ring-offset-2",
1080
1076
  ),
1081
1077
  onBlur: closeCompletionHandler,
1082
1078
  onKeyDown: resumeCompletionHandler,
@@ -1086,7 +1082,11 @@ const SetupCellComponent = ({
1086
1082
  tabIndex={-1}
1087
1083
  data-setup-cell={true}
1088
1084
  >
1089
- <div className={cn("tray")} data-hidden={!isCellCodeShown}>
1085
+ <div
1086
+ className={cn("tray")}
1087
+ data-has-output-above={false}
1088
+ data-hidden={!isCellCodeShown}
1089
+ >
1090
1090
  <StagedAICellBackground
1091
1091
  cellId={cellId}
1092
1092
  className="mo-ai-setup-cell"
@@ -12,6 +12,7 @@ import { getInitialAppMode } from "@/core/mode";
12
12
  import { CssVariables } from "@/theme/ThemeProvider";
13
13
  import { reactLazyWithPreload } from "@/utils/lazy";
14
14
  import { ErrorBoundary } from "../components/editor/boundary/ErrorBoundary";
15
+ import { KernelStartupErrorModal } from "../components/editor/KernelStartupErrorModal";
15
16
  import { ModalProvider } from "../components/modal/ImperativeModal";
16
17
  import { Toaster } from "../components/ui/toaster";
17
18
  import { TooltipProvider } from "../components/ui/tooltip";
@@ -91,6 +92,7 @@ const Providers = memo(({ children }: PropsWithChildren) => {
91
92
  {children}
92
93
  <Toaster />
93
94
  <TailwindIndicator />
95
+ <KernelStartupErrorModal />
94
96
  </ModalProvider>
95
97
  </LocaleProvider>
96
98
  </SlotzProvider>
@@ -1325,10 +1325,7 @@ const {
1325
1325
  };
1326
1326
  },
1327
1327
  addSetupCellIfDoesntExist: (state, action: { code?: string }) => {
1328
- let { code } = action;
1329
- if (code == null) {
1330
- code = "# Initialization code that runs before all other cells";
1331
- }
1328
+ const { code } = action;
1332
1329
 
1333
1330
  if (state.cellIds.setupCellExists()) {
1334
1331
  // Just focus on the existing setup cell
@@ -1,11 +1,17 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
- import { useAtomValue } from "jotai";
3
+ import { atom, useAtomValue } from "jotai";
4
4
  import { createReducerAndAtoms } from "@/utils/createReducer";
5
5
  import type { Identified } from "@/utils/typed";
6
6
  import { generateUUID } from "@/utils/uuid";
7
7
  import type { Banner } from "../kernel/messages";
8
8
 
9
+ /**
10
+ * Atom for storing kernel startup error message.
11
+ * When set to a non-null value, shows a modal with the error details.
12
+ */
13
+ export const kernelStartupErrorAtom = atom<string | null>(null);
14
+
9
15
  interface BannerState {
10
16
  banners: Identified<Banner>[];
11
17
  }
@@ -192,6 +192,8 @@ export async function initialize() {
192
192
  return;
193
193
  case "cache-info":
194
194
  return;
195
+ case "kernel-startup-error":
196
+ return;
195
197
  default:
196
198
  logNever(msg.data);
197
199
  }
@@ -16,6 +16,7 @@ export const WebSocketClosedReason = {
16
16
  KERNEL_DISCONNECTED: "KERNEL_DISCONNECTED",
17
17
  ALREADY_RUNNING: "ALREADY_RUNNING",
18
18
  MALFORMED_QUERY: "MALFORMED_QUERY",
19
+ KERNEL_STARTUP_ERROR: "KERNEL_STARTUP_ERROR",
19
20
  } as const;
20
21
 
21
22
  export type WebSocketClosedReason =
@@ -40,7 +40,7 @@ import {
40
40
  } from "../datasets/request-registry";
41
41
  import { useDatasetsActions } from "../datasets/state";
42
42
  import { UI_ELEMENT_REGISTRY } from "../dom/uiregistry";
43
- import { useBannersActions } from "../errors/state";
43
+ import { kernelStartupErrorAtom, useBannersActions } from "../errors/state";
44
44
  import { FUNCTIONS_REGISTRY } from "../functions/FunctionRegistry";
45
45
  import {
46
46
  handleCellNotificationeration,
@@ -105,6 +105,7 @@ export function useMarimoKernelConnection(opts: {
105
105
  const setCapabilities = useSetAtom(capabilitiesAtom);
106
106
  const runtimeManager = useRuntimeManager();
107
107
  const setCacheInfo = useSetAtom(cacheInfoAtom);
108
+ const setKernelStartupError = useSetAtom(kernelStartupErrorAtom);
108
109
 
109
110
  const handleMessage = (e: MessageEvent<JsonString<NotificationPayload>>) => {
110
111
  const msg = jsonParseWithSpecialChar(e.data);
@@ -134,6 +135,11 @@ export function useMarimoKernelConnection(opts: {
134
135
  case "interrupted":
135
136
  return;
136
137
 
138
+ case "kernel-startup-error":
139
+ // Full error received via message before websocket close
140
+ setKernelStartupError(msg.data.error);
141
+ return;
142
+
137
143
  case "send-ui-element-message": {
138
144
  const modelId = msg.data.model_id;
139
145
  const uiElement = msg.data.ui_element;
@@ -413,6 +419,17 @@ export function useMarimoKernelConnection(opts: {
413
419
  return;
414
420
 
415
421
  default:
422
+ // Check for kernel startup error (full error already received via message)
423
+ if (e.reason === "MARIMO_KERNEL_STARTUP_ERROR") {
424
+ setConnection({
425
+ state: WebSocketState.CLOSED,
426
+ code: WebSocketClosedReason.KERNEL_STARTUP_ERROR,
427
+ reason: "Failed to start kernel sandbox",
428
+ });
429
+ ws.close(); // prevent reconnecting
430
+ return;
431
+ }
432
+
416
433
  // Session should be valid
417
434
  // - browser tab might have been closed or re-opened
418
435
  // - computer might have just woken from sleep
@@ -319,6 +319,12 @@
319
319
  }
320
320
  }
321
321
 
322
+ #cell-setup,
323
+ #cell-setup .cm-editor,
324
+ #cell-setup .cm-gutter {
325
+ background-color: var(--gray-2);
326
+ }
327
+
322
328
  #App.disconnected {
323
329
  /* Background determined by disconnected gradient/noise. */
324
330