@marimo-team/islands 0.24.3-dev3 → 0.24.3-dev5

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.
@@ -1,10 +1,22 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
- import { act, renderHook } from "@testing-library/react";
2
+ import {
3
+ act,
4
+ fireEvent,
5
+ render,
6
+ renderHook,
7
+ screen,
8
+ } from "@testing-library/react";
3
9
  import { beforeEach, describe, expect, it, vi } from "vitest";
4
10
  import { useInstallPackages } from "../useInstallPackage";
11
+ import { Toast, ToastProvider, ToastViewport } from "@/components/ui/toast";
5
12
 
6
13
  const toast = vi.fn();
7
14
  const addPackage = vi.fn();
15
+ const restartKernel = vi.fn();
16
+
17
+ vi.mock("@/components/editor/actions/useRestartKernel", () => ({
18
+ useRestartKernel: () => restartKernel,
19
+ }));
8
20
 
9
21
  vi.mock("@/components/ui/use-toast", () => ({
10
22
  toast: (...args: unknown[]) => toast(...args),
@@ -20,6 +32,7 @@ describe("useInstallPackages", () => {
20
32
  beforeEach(() => {
21
33
  toast.mockClear();
22
34
  addPackage.mockClear();
35
+ restartKernel.mockClear();
23
36
  });
24
37
 
25
38
  it("batches all packages into a single install call", async () => {
@@ -109,6 +122,39 @@ describe("useInstallPackages", () => {
109
122
  );
110
123
  });
111
124
 
125
+ it("reports saved changes requiring restart without claiming installation", async () => {
126
+ addPackage.mockResolvedValue({
127
+ success: false,
128
+ error: null,
129
+ restartRequired: true,
130
+ });
131
+ const { result } = renderHook(() => useInstallPackages());
132
+ await act(async () => {
133
+ await result.current.handleInstallPackages(["boltons", "six"]);
134
+ });
135
+ expect(toast).toHaveBeenCalledTimes(1);
136
+ expect(toast).toHaveBeenCalledWith(
137
+ expect.objectContaining({
138
+ title: "Changes saved — restart required",
139
+ description: expect.stringContaining(
140
+ "Restarting clears in-memory variables",
141
+ ),
142
+ duration: Infinity,
143
+ action: expect.anything(),
144
+ }),
145
+ );
146
+ expect(toast.mock.calls[0][0].variant).not.toBe("danger");
147
+ render(
148
+ <ToastProvider>
149
+ <Toast>{toast.mock.calls[0][0].action}</Toast>
150
+ <ToastViewport />
151
+ </ToastProvider>,
152
+ );
153
+ expect(restartKernel).not.toHaveBeenCalled();
154
+ fireEvent.click(screen.getByRole("button", { name: "Restart Kernel" }));
155
+ expect(restartKernel).toHaveBeenCalledTimes(1);
156
+ });
157
+
112
158
  it("calls onSuccess after a successful install", async () => {
113
159
  addPackage.mockResolvedValue({ success: true, error: null });
114
160
  const onSuccess = vi.fn();
@@ -1,8 +1,34 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
+ import { useRestartKernel } from "@/components/editor/actions/useRestartKernel";
3
4
  import { Kbd } from "@/components/ui/kbd";
5
+ import { ToastAction } from "@/components/ui/toast";
4
6
  import { toast } from "@/components/ui/use-toast";
5
7
 
8
+ export const RESTART_REQUIRED_DESCRIPTION =
9
+ "Your dependency changes are saved. Restart the kernel to use the updated environment. Restarting clears in-memory variables.";
10
+
11
+ const RestartKernelAction = () => {
12
+ const restartKernel = useRestartKernel();
13
+ return (
14
+ <ToastAction
15
+ altText="Restart the kernel to use the updated dependencies"
16
+ onClick={restartKernel}
17
+ >
18
+ Restart Kernel
19
+ </ToastAction>
20
+ );
21
+ };
22
+
23
+ export const showPackageRestartToast = () => {
24
+ toast({
25
+ title: "Changes saved — restart required",
26
+ description: RESTART_REQUIRED_DESCRIPTION,
27
+ duration: Infinity,
28
+ action: <RestartKernelAction />,
29
+ });
30
+ };
31
+
6
32
  export const showAddPackageToast = (
7
33
  packageName: string | string[],
8
34
  error?: string | null,
@@ -3,7 +3,10 @@
3
3
  import { useState } from "react";
4
4
  import { Logger } from "@/utils/Logger";
5
5
  import { useRequestClient } from "../network/requests";
6
- import { showAddPackageToast } from "./toast-components";
6
+ import {
7
+ showAddPackageToast,
8
+ showPackageRestartToast,
9
+ } from "./toast-components";
7
10
 
8
11
  export function useInstallPackages(): {
9
12
  handleInstallPackages: (
@@ -30,7 +33,9 @@ export function useInstallPackages(): {
30
33
  // response only carries an aggregate success/error. Report a single
31
34
  // toast covering all requested packages rather than implying a
32
35
  // per-package outcome we don't actually have.
33
- if (response.success) {
36
+ if (response.restartRequired) {
37
+ showPackageRestartToast();
38
+ } else if (response.success) {
34
39
  showAddPackageToast(packages);
35
40
  } else {
36
41
  showAddPackageToast(packages, response.error);
@@ -649,6 +649,7 @@ export class PyodideBridge implements RunRequests, EditRequests {
649
649
  tags: [],
650
650
  version: null,
651
651
  },
652
+ context: { kind: "package-manager", name: "micropip" },
652
653
  };
653
654
  };
654
655