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

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.24.3-dev83",
3
+ "version": "0.24.3-dev88",
4
4
  "main": "dist/main.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -6,7 +6,7 @@ import { pairPreviewSchema } from "@/core/config/pair";
6
6
 
7
7
  // Read the Python-owned resource only in tests to check the shared contract.
8
8
  export const PAIR_PREVIEW = pairPreviewSchema.parse({
9
- command: "uv run marimo",
9
+ command: "uvx marimo@latest",
10
10
  templates: JSON.parse(
11
11
  readFileSync(
12
12
  path.resolve(
@@ -129,7 +129,7 @@ describe("getTerminalCommand", () => {
129
129
  PAIR_PREVIEW,
130
130
  ),
131
131
  ).toBe(
132
- String.raw`codex "$(MARIMO_PAIR_NEXT=1 uv run marimo pair prompt \
132
+ String.raw`codex "$(MARIMO_PAIR_NEXT=1 uvx marimo@latest pair prompt \
133
133
  --url http://localhost:8000)"`,
134
134
  );
135
135
  } finally {
@@ -152,7 +152,7 @@ describe("getTerminalCommand", () => {
152
152
  PAIR_PREVIEW,
153
153
  ),
154
154
  ).toBe(
155
- String.raw`${cli} "$(MARIMO_PAIR_NEXT=1 uv run marimo pair prompt \
155
+ String.raw`${cli} "$(MARIMO_PAIR_NEXT=1 uvx marimo@latest pair prompt \
156
156
  --url http://localhost:8000 \
157
157
  --file notebooks/example.py \
158
158
  --session s_ab12cd \
@@ -264,8 +264,8 @@ describe("getRawPrompt", () => {
264
264
  expect(getRawPrompt(connection, null, PAIR_PREVIEW)).toBe(
265
265
  "Pair with me on this running marimo notebook.\n\n" +
266
266
  `URL: http://localhost:8000\n${fields}\n` +
267
- "Run `uv run marimo pair --help` first.\n" +
268
- "Use `uv run marimo` for all marimo commands.\n\n" +
267
+ "Run `uvx marimo@latest pair --help` first.\n" +
268
+ "Use `uvx marimo@latest` for all marimo commands.\n\n" +
269
269
  "Once connected, send a fun toast using `mo.status.toast(...)` (`import marimo as mo`).",
270
270
  );
271
271
  });
@@ -278,8 +278,8 @@ describe("getRawPrompt", () => {
278
278
  "URL: http://localhost:8000\n" +
279
279
  "File: notebooks/example.py\n" +
280
280
  "Session: s_ab12cd\n\n" +
281
- "Run `uv run marimo pair --help` first.\n" +
282
- "Use `uv run marimo` for all marimo commands.\n\n" +
281
+ "Run `uvx marimo@latest pair --help` first.\n" +
282
+ "Use `uvx marimo@latest` for all marimo commands.\n\n" +
283
283
  "Once connected, send a fun toast using `mo.status.toast(...)` (`import marimo as mo`).",
284
284
  );
285
285
  });
@@ -203,7 +203,7 @@ describe("PairWithAgentModal", () => {
203
203
  );
204
204
  await waitFor(() =>
205
205
  expect(copyToClipboard).toHaveBeenCalledWith(
206
- String.raw`claude "$(MARIMO_PAIR_NEXT=1 uv run marimo pair prompt \
206
+ String.raw`claude "$(MARIMO_PAIR_NEXT=1 uvx marimo@latest pair prompt \
207
207
  --url http://localhost:8000/ \
208
208
  --file notebook.py \
209
209
  --session ${getSessionId()} \
@@ -64,7 +64,6 @@
64
64
  padding-top: 2px;
65
65
  padding-bottom: 2px;
66
66
  overflow: auto;
67
- overscroll-behavior: contain;
68
67
  @apply text-xs;
69
68
 
70
69
  .w-rjv-wrap > :not(:last-child) {
@@ -7,7 +7,7 @@ const Table = React.forwardRef<
7
7
  HTMLTableElement,
8
8
  React.HTMLAttributes<HTMLTableElement>
9
9
  >(({ className, ...props }, ref) => (
10
- <div className="w-full overflow-auto overscroll-contain scrollbar-thin flex-1 print:overflow-hidden">
10
+ <div className="w-full overflow-auto scrollbar-thin flex-1 print:overflow-hidden">
11
11
  <table
12
12
  ref={ref}
13
13
  className={cn("w-full caption-bottom text-sm", className)}
@@ -82,7 +82,6 @@
82
82
  .console-output-area {
83
83
  max-height: 610px;
84
84
  overflow: auto;
85
- overscroll-behavior: contain;
86
85
  }
87
86
 
88
87
  /* Special case for particular components */
@@ -522,7 +521,6 @@
522
521
  display: flow-root;
523
522
 
524
523
  overflow: auto;
525
- overscroll-behavior: contain;
526
524
  }
527
525
 
528
526
  .marimo-output-stale,
@@ -218,7 +218,7 @@ export const FileBrowser = ({
218
218
 
219
219
  const { data, error, isPending } = useAsyncData(() => {
220
220
  return list_directory({ path: path });
221
- }, [path, randomId]);
221
+ }, [path, randomId, list_directory]);
222
222
 
223
223
  useEffect(() => {
224
224
  if (!isPending) {
@@ -238,9 +238,15 @@ export const FileBrowser = ({
238
238
  // Reset the roving tabindex whenever the listing reloads (a new path or a
239
239
  // same-path refresh) so activeIndex never points past the current rows.
240
240
  const listingKey = `${path}::${randomId}`;
241
- const [prevListingKey, setPrevListingKey] = useState(listingKey);
242
- if (prevListingKey !== listingKey) {
243
- setPrevListingKey(listingKey);
241
+ const [prevListing, setPrevListing] = useState({
242
+ listingKey,
243
+ list_directory,
244
+ });
245
+ if (
246
+ prevListing.listingKey !== listingKey ||
247
+ prevListing.list_directory !== list_directory
248
+ ) {
249
+ setPrevListing({ listingKey, list_directory });
244
250
  setActiveIndex(0);
245
251
  }
246
252
 
@@ -5,8 +5,9 @@ import { beforeAll, describe, expect, it, vi } from "vitest";
5
5
  import { SetupMocks } from "@/__mocks__/common";
6
6
  import { initialModeAtom } from "@/core/mode";
7
7
  import { store } from "@/core/state/jotai";
8
+ import { CellNotInitializedError } from "@/utils/errors";
8
9
  import type { IPluginProps } from "../../types";
9
- import { FileBrowserPlugin } from "../FileBrowserPlugin";
10
+ import { FileBrowser, FileBrowserPlugin } from "../FileBrowserPlugin";
10
11
 
11
12
  interface MockFile {
12
13
  id: string;
@@ -80,6 +81,54 @@ beforeAll(() => {
80
81
  store.set(initialModeAtom, "edit");
81
82
  });
82
83
 
84
+ describe("FileBrowserPlugin session cache recovery", () => {
85
+ it("retries a failed cached listing when the plugin resets", async () => {
86
+ const host = document.createElement("marimo-file-browser");
87
+ const wrapper = document.createElement("marimo-ui-element");
88
+ wrapper.setAttribute("random-id", "cached-id");
89
+ wrapper.append(host);
90
+
91
+ const props = {
92
+ initialPath: "/home/user",
93
+ filetypes: [],
94
+ selectionMode: "directory",
95
+ multiple: false,
96
+ label: "Pick folder",
97
+ restrictNavigation: false,
98
+ value: [],
99
+ setValue: vi.fn(),
100
+ host,
101
+ };
102
+ const error = new CellNotInitializedError();
103
+ const cachedListDirectory = vi.fn().mockRejectedValue(error);
104
+ const { rerender } = render(
105
+ <FileBrowser {...props} list_directory={cachedListDirectory} />,
106
+ );
107
+ expect(await screen.findByText(error.message)).toBeInTheDocument();
108
+
109
+ // PluginSlot recreates the RPC functions on reset, even when the path
110
+ // and the host's attributes are unchanged.
111
+ const liveListDirectory = mockListDirectory(FILES);
112
+ rerender(<FileBrowser {...props} list_directory={liveListDirectory} />);
113
+
114
+ expect(await screen.findByText("docs")).toBeInTheDocument();
115
+ expect(screen.queryByText(error.message)).not.toBeInTheDocument();
116
+ expect(liveListDirectory.mock.calls).toEqual([[{ path: "/home/user" }]]);
117
+
118
+ fireEvent.click(screen.getByRole("checkbox", { name: "Select docs" }));
119
+ expect(props.setValue).toHaveBeenCalledWith([
120
+ { ...FILES[0], id: FILES[0].path },
121
+ ]);
122
+
123
+ fireEvent.keyDown(screen.getAllByRole("row")[0], { key: "End" });
124
+ rerender(
125
+ <FileBrowser {...props} list_directory={mockListDirectory([FILES[0]])} />,
126
+ );
127
+ await waitFor(() => expect(screen.getAllByRole("row")).toHaveLength(2));
128
+ expect(screen.getAllByRole("row")[0]).toHaveAttribute("tabindex", "0");
129
+ });
130
+ });
131
+
83
132
  describe("FileBrowserPlugin keyboard accessibility", () => {
84
133
  it("renders a row per file plus the parent row", async () => {
85
134
  renderBrowser();