@marimo-team/islands 0.19.3-dev30 → 0.19.3-dev34
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/main.js +2 -2
- package/package.json +1 -1
- package/src/__tests__/mount.test.ts +128 -0
- package/src/components/data-table/column-header.tsx +1 -1
- package/src/components/editor/alerts/connecting-alert.tsx +33 -6
- package/src/core/network/__tests__/requests-network.test.ts +0 -18
- package/src/core/network/requests-network.ts +2 -7
- package/src/mount.tsx +6 -0
package/dist/main.js
CHANGED
|
@@ -56281,7 +56281,7 @@ Database schema: ${c}`), (_a3 = r2.aiFix) == null ? void 0 : _a3.setAiCompletion
|
|
|
56281
56281
|
shouldFilter: false,
|
|
56282
56282
|
children: [
|
|
56283
56283
|
(0, import_jsx_runtime.jsx)(CommandInput, {
|
|
56284
|
-
placeholder:
|
|
56284
|
+
placeholder: `Search among the top ${v.length} values`,
|
|
56285
56285
|
autoFocus: true,
|
|
56286
56286
|
onValueChange: (e3) => _(e3.trim())
|
|
56287
56287
|
}),
|
|
@@ -101155,7 +101155,7 @@ Defaulting to \`null\`.`;
|
|
|
101155
101155
|
return Logger.warn("Failed to get version from mount config"), null;
|
|
101156
101156
|
}
|
|
101157
101157
|
}
|
|
101158
|
-
const marimoVersionAtom = atom(getVersionFromMountConfig() || "0.19.3-
|
|
101158
|
+
const marimoVersionAtom = atom(getVersionFromMountConfig() || "0.19.3-dev34"), showCodeInRunModeAtom = atom(true);
|
|
101159
101159
|
atom(null);
|
|
101160
101160
|
var VIRTUAL_FILE_REGEX = /\/@file\/([^\s"&'/]+)\.([\dA-Za-z]+)/g, VirtualFileTracker = class e {
|
|
101161
101161
|
constructor() {
|
package/package.json
CHANGED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
4
|
+
import { connectionAtom } from "@/core/network/connection";
|
|
5
|
+
import { store } from "@/core/state/jotai";
|
|
6
|
+
import { WebSocketState } from "@/core/websocket/types";
|
|
7
|
+
import { mount, visibleForTesting } from "../mount";
|
|
8
|
+
|
|
9
|
+
// Mock React DOM
|
|
10
|
+
vi.mock("react-dom/client", () => ({
|
|
11
|
+
createRoot: vi.fn(() => ({
|
|
12
|
+
render: vi.fn(),
|
|
13
|
+
})),
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
// Mock static state
|
|
17
|
+
vi.mock("@/core/static/static-state", () => ({
|
|
18
|
+
isStaticNotebook: vi.fn(() => false),
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
// Mock other side-effect modules
|
|
22
|
+
vi.mock("@/core/vscode/vscode-bindings", () => ({
|
|
23
|
+
maybeRegisterVSCodeBindings: vi.fn(),
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
vi.mock("@/plugins/plugins", () => ({
|
|
27
|
+
initializePlugins: vi.fn(),
|
|
28
|
+
}));
|
|
29
|
+
|
|
30
|
+
vi.mock("@/core/network/auth", () => ({
|
|
31
|
+
cleanupAuthQueryParams: vi.fn(),
|
|
32
|
+
}));
|
|
33
|
+
|
|
34
|
+
vi.mock("@/utils/vitals", () => ({
|
|
35
|
+
reportVitals: vi.fn(),
|
|
36
|
+
}));
|
|
37
|
+
|
|
38
|
+
// Mock preloadPage
|
|
39
|
+
vi.mock("@/core/MarimoApp", () => ({
|
|
40
|
+
MarimoApp: () => null,
|
|
41
|
+
preloadPage: vi.fn(),
|
|
42
|
+
}));
|
|
43
|
+
|
|
44
|
+
describe("mount", () => {
|
|
45
|
+
const mockElement = document.createElement("div");
|
|
46
|
+
|
|
47
|
+
beforeEach(() => {
|
|
48
|
+
visibleForTesting.reset();
|
|
49
|
+
// Reset connection atom to initial state
|
|
50
|
+
store.set(connectionAtom, { state: WebSocketState.NOT_STARTED });
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
afterEach(() => {
|
|
54
|
+
vi.clearAllMocks();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const baseOptions = {
|
|
58
|
+
filename: "test.py",
|
|
59
|
+
code: "",
|
|
60
|
+
version: "0.0.1",
|
|
61
|
+
mode: "edit" as const,
|
|
62
|
+
config: {},
|
|
63
|
+
configOverrides: {},
|
|
64
|
+
appConfig: {},
|
|
65
|
+
view: { showAppCode: true },
|
|
66
|
+
serverToken: "",
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
describe("connection state initialization", () => {
|
|
70
|
+
it("should set connection to CONNECTING when runtimeConfig has lazy=false", () => {
|
|
71
|
+
mount(
|
|
72
|
+
{
|
|
73
|
+
...baseOptions,
|
|
74
|
+
runtimeConfig: [{ url: "http://localhost:8080", lazy: false }],
|
|
75
|
+
},
|
|
76
|
+
mockElement,
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const connection = store.get(connectionAtom);
|
|
80
|
+
expect(connection.state).toBe(WebSocketState.CONNECTING);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("should keep connection as NOT_STARTED when runtimeConfig has lazy=true", () => {
|
|
84
|
+
mount(
|
|
85
|
+
{
|
|
86
|
+
...baseOptions,
|
|
87
|
+
runtimeConfig: [{ url: "http://localhost:8080", lazy: true }],
|
|
88
|
+
},
|
|
89
|
+
mockElement,
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const connection = store.get(connectionAtom);
|
|
93
|
+
expect(connection.state).toBe(WebSocketState.NOT_STARTED);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("should keep connection as NOT_STARTED when no runtimeConfig is provided", () => {
|
|
97
|
+
mount(
|
|
98
|
+
{
|
|
99
|
+
...baseOptions,
|
|
100
|
+
runtimeConfig: [],
|
|
101
|
+
},
|
|
102
|
+
mockElement,
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
const connection = store.get(connectionAtom);
|
|
106
|
+
expect(connection.state).toBe(WebSocketState.NOT_STARTED);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("should keep connection as NOT_STARTED for static notebooks even with lazy=false", async () => {
|
|
110
|
+
const { isStaticNotebook } = await import("@/core/static/static-state");
|
|
111
|
+
vi.mocked(isStaticNotebook).mockReturnValue(true);
|
|
112
|
+
|
|
113
|
+
// Reset mount state to allow another mount
|
|
114
|
+
visibleForTesting.reset();
|
|
115
|
+
|
|
116
|
+
mount(
|
|
117
|
+
{
|
|
118
|
+
...baseOptions,
|
|
119
|
+
runtimeConfig: [{ url: "http://localhost:8080", lazy: false }],
|
|
120
|
+
},
|
|
121
|
+
mockElement,
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
const connection = store.get(connectionAtom);
|
|
125
|
+
expect(connection.state).toBe(WebSocketState.NOT_STARTED);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
});
|
|
@@ -593,7 +593,7 @@ const PopoverFilterByValues = <TData, TValue>({
|
|
|
593
593
|
<>
|
|
594
594
|
<Command className="text-sm outline-hidden" shouldFilter={false}>
|
|
595
595
|
<CommandInput
|
|
596
|
-
placeholder=
|
|
596
|
+
placeholder={`Search among the top ${data.length} values`}
|
|
597
597
|
autoFocus={true}
|
|
598
598
|
onValueChange={(value) => setQuery(value.trim())}
|
|
599
599
|
/>
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useAtomValue } from "jotai";
|
|
4
4
|
import { LoadingEllipsis } from "@/components/icons/loading-ellipsis";
|
|
5
|
+
import { Spinner } from "@/components/icons/spinner";
|
|
5
6
|
import { Button } from "@/components/ui/button";
|
|
6
7
|
import { DelayMount } from "@/components/utils/delay-mount";
|
|
7
8
|
import {
|
|
@@ -10,24 +11,50 @@ import {
|
|
|
10
11
|
isNotStartedAtom,
|
|
11
12
|
} from "@/core/network/connection";
|
|
12
13
|
import { useConnectToRuntime } from "@/core/runtime/config";
|
|
14
|
+
import { Banner } from "@/plugins/impl/common/error-banner";
|
|
13
15
|
import { Tooltip } from "../../ui/tooltip";
|
|
14
16
|
import { FloatingAlert } from "./floating-alert";
|
|
15
17
|
|
|
16
18
|
const SHORT_DELAY_MS = 1000; // 1 second
|
|
19
|
+
const LONG_DELAY_MS = 5000; // 5 seconds
|
|
17
20
|
|
|
18
21
|
export const ConnectingAlert: React.FC = () => {
|
|
19
22
|
const isConnecting = useAtomValue(isConnectingAtom);
|
|
20
23
|
const isClosed = useAtomValue(isClosedAtom);
|
|
21
24
|
|
|
22
25
|
if (isConnecting) {
|
|
26
|
+
const subtleNotification = (
|
|
27
|
+
<Tooltip content="Connecting to a marimo runtime">
|
|
28
|
+
<div className="flex items-center">
|
|
29
|
+
<LoadingEllipsis size={5} className="text-yellow-500" />
|
|
30
|
+
</div>
|
|
31
|
+
</Tooltip>
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const longNotification = (
|
|
35
|
+
<Banner
|
|
36
|
+
kind="info"
|
|
37
|
+
className="flex flex-col rounded py-2 px-4 animate-in slide-in-from-top w-fit"
|
|
38
|
+
>
|
|
39
|
+
<div className="flex flex-col gap-4 justify-between items-start text-muted-foreground text-base">
|
|
40
|
+
<div className="flex items-center gap-2">
|
|
41
|
+
<Spinner className="h-4 w-4" />
|
|
42
|
+
<p>Connecting to a marimo runtime ...</p>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
</Banner>
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
// This waits for 1 second to show the subtle notification, then shows the long notification after 5 seconds.
|
|
23
49
|
return (
|
|
24
50
|
<DelayMount milliseconds={SHORT_DELAY_MS}>
|
|
25
|
-
<div className="
|
|
26
|
-
<
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
51
|
+
<div className="m-0 flex items-center min-h-[28px] fixed top-5 left-1/2 transform -translate-x-1/2 z-200">
|
|
52
|
+
<DelayMount
|
|
53
|
+
milliseconds={LONG_DELAY_MS}
|
|
54
|
+
fallback={subtleNotification}
|
|
55
|
+
>
|
|
56
|
+
{longNotification}
|
|
57
|
+
</DelayMount>
|
|
31
58
|
</div>
|
|
32
59
|
</DelayMount>
|
|
33
60
|
);
|
|
@@ -34,12 +34,6 @@ vi.mock("../connection", () => ({
|
|
|
34
34
|
waitForConnectionOpen: vi.fn().mockResolvedValue(undefined),
|
|
35
35
|
}));
|
|
36
36
|
|
|
37
|
-
vi.mock("../../state/jotai", () => ({
|
|
38
|
-
store: {
|
|
39
|
-
get: vi.fn(() => true),
|
|
40
|
-
},
|
|
41
|
-
}));
|
|
42
|
-
|
|
43
37
|
describe("createNetworkRequests", () => {
|
|
44
38
|
let mockClient: any;
|
|
45
39
|
let capturedCalls: Map<string, { hasParams: boolean; endpoint: string }>;
|
|
@@ -80,18 +74,6 @@ describe("createNetworkRequests", () => {
|
|
|
80
74
|
});
|
|
81
75
|
|
|
82
76
|
describe("special behavior", () => {
|
|
83
|
-
it("sendRun should drop requests if not connected", async () => {
|
|
84
|
-
const { store } = await import("../../state/jotai");
|
|
85
|
-
|
|
86
|
-
vi.mocked(store.get).mockReturnValue(false);
|
|
87
|
-
|
|
88
|
-
const requests = createNetworkRequests();
|
|
89
|
-
const result = await requests.sendRun({} as any);
|
|
90
|
-
|
|
91
|
-
expect(result).toBe(null);
|
|
92
|
-
expect(mockClient.POST).not.toHaveBeenCalled();
|
|
93
|
-
});
|
|
94
|
-
|
|
95
77
|
it("exportAsHTML should set assetUrl in dev/test mode", async () => {
|
|
96
78
|
const originalEnv = process.env.NODE_ENV;
|
|
97
79
|
process.env.NODE_ENV = "development";
|
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import { once } from "lodash-es";
|
|
4
4
|
import { getRuntimeManager } from "../runtime/config";
|
|
5
|
-
import { store } from "../state/jotai";
|
|
6
5
|
import { API, createClientWithRuntimeManager } from "./api";
|
|
7
|
-
import {
|
|
6
|
+
import { waitForConnectionOpen } from "./connection";
|
|
8
7
|
import type { EditRequests, RunRequests } from "./types";
|
|
9
8
|
|
|
10
9
|
const { handleResponse, handleResponseReturnNull } = API;
|
|
@@ -105,11 +104,7 @@ export function createNetworkRequests(): EditRequests & RunRequests {
|
|
|
105
104
|
.then(handleResponseReturnNull);
|
|
106
105
|
},
|
|
107
106
|
sendRun: async (request) => {
|
|
108
|
-
|
|
109
|
-
// Otherwise we can get into a weird state of sending requests for cells that no longer exist.
|
|
110
|
-
if (!store.get(isConnectedAtom)) {
|
|
111
|
-
return null;
|
|
112
|
-
}
|
|
107
|
+
await waitForConnectionOpen();
|
|
113
108
|
return getClient()
|
|
114
109
|
.POST("/api/kernel/run", {
|
|
115
110
|
body: request,
|
package/src/mount.tsx
CHANGED
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
import { MarimoApp, preloadPage } from "./core/MarimoApp";
|
|
30
30
|
import { type AppMode, initialModeAtom, viewStateAtom } from "./core/mode";
|
|
31
31
|
import { cleanupAuthQueryParams } from "./core/network/auth";
|
|
32
|
+
import { connectionAtom } from "./core/network/connection";
|
|
32
33
|
import { requestClientAtom } from "./core/network/requests";
|
|
33
34
|
import { resolveRequestClient } from "./core/network/resolve";
|
|
34
35
|
import {
|
|
@@ -42,6 +43,7 @@ import { isStaticNotebook } from "./core/static/static-state";
|
|
|
42
43
|
import { maybeRegisterVSCodeBindings } from "./core/vscode/vscode-bindings";
|
|
43
44
|
import type { FileStore } from "./core/wasm/store";
|
|
44
45
|
import { notebookFileStore } from "./core/wasm/store";
|
|
46
|
+
import { WebSocketState } from "./core/websocket/types";
|
|
45
47
|
import { vegaLoader } from "./plugins/impl/vega/loader";
|
|
46
48
|
import { initializePlugins } from "./plugins/plugins";
|
|
47
49
|
import { ThemeProvider } from "./theme/ThemeProvider";
|
|
@@ -304,6 +306,10 @@ function initStore(options: unknown) {
|
|
|
304
306
|
...firstRuntimeConfig,
|
|
305
307
|
serverToken: parsedOptions.data.serverToken,
|
|
306
308
|
});
|
|
309
|
+
// If the remote runtime is not lazy, start it in CONNECTING
|
|
310
|
+
if (!firstRuntimeConfig.lazy && !isStaticNotebook()) {
|
|
311
|
+
store.set(connectionAtom, { state: WebSocketState.CONNECTING });
|
|
312
|
+
}
|
|
307
313
|
} else {
|
|
308
314
|
store.set(runtimeConfigAtom, {
|
|
309
315
|
...DEFAULT_RUNTIME_CONFIG,
|