@marimo-team/islands 0.21.2-dev6 → 0.21.2-dev62
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/{ConnectedDataExplorerComponent-D0GoOd_c.js → ConnectedDataExplorerComponent-DrWDbHRV.js} +1 -1
- package/dist/{any-language-editor-DlsjUw_l.js → any-language-editor-BRpxklRq.js} +1 -1
- package/dist/{copy-DIK6DiIA.js → copy-BjkXCUxP.js} +12 -2
- package/dist/{esm-BLobyqMs.js → esm-No_6eSQS.js} +1 -1
- package/dist/{glide-data-editor-pZyd9UJ_.js → glide-data-editor-858wsVkd.js} +1 -1
- package/dist/main.js +821 -417
- package/dist/{spec-Bfvf9Hre.js → spec-oVDndBz4.js} +25 -16
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/__mocks__/notebook.ts +9 -9
- package/src/__mocks__/requests.ts +1 -0
- package/src/__tests__/branded.ts +20 -0
- package/src/components/app-config/user-config-form.tsx +5 -4
- package/src/components/data-table/__tests__/utils.test.ts +138 -1
- package/src/components/data-table/charts/__tests__/storage.test.ts +7 -7
- package/src/components/data-table/context-menu.tsx +9 -5
- package/src/components/data-table/data-table.tsx +3 -0
- package/src/components/data-table/range-focus/__tests__/atoms.test.ts +8 -2
- package/src/components/data-table/range-focus/__tests__/test-utils.ts +2 -0
- package/src/components/data-table/range-focus/__tests__/utils.test.ts +82 -8
- package/src/components/data-table/range-focus/atoms.ts +2 -2
- package/src/components/data-table/range-focus/utils.ts +50 -12
- package/src/components/data-table/types.ts +7 -0
- package/src/components/data-table/utils.ts +87 -0
- package/src/components/editor/__tests__/data-attributes.test.tsx +8 -8
- package/src/components/editor/ai/__tests__/completion-utils.test.ts +15 -15
- package/src/components/editor/connections/storage/__tests__/__snapshots__/as-code.test.ts.snap +2 -2
- package/src/components/editor/connections/storage/as-code.ts +2 -2
- package/src/components/editor/file-tree/file-explorer.tsx +16 -2
- package/src/components/editor/file-tree/file-viewer.tsx +17 -3
- package/src/components/editor/navigation/__tests__/clipboard.test.ts +2 -2
- package/src/components/editor/navigation/__tests__/selection.test.ts +7 -6
- package/src/components/editor/navigation/__tests__/state.test.ts +8 -7
- package/src/components/editor/output/MarimoErrorOutput.tsx +7 -7
- package/src/components/editor/output/__tests__/traceback.test.tsx +4 -4
- package/src/components/editor/output/console/__tests__/ConsoleOutput.test.tsx +4 -4
- package/src/components/editor/renderers/vertical-layout/useFocusFirstEditor.ts +8 -1
- package/src/components/storage/storage-file-viewer.tsx +35 -1
- package/src/components/storage/storage-inspector.tsx +9 -4
- package/src/components/storage/storage-snippets.ts +3 -3
- package/src/components/tracing/tracing.tsx +3 -1
- package/src/components/ui/range-slider.tsx +108 -1
- package/src/core/ai/__tests__/staged-cells.test.ts +9 -8
- package/src/core/ai/context/providers/__tests__/cell-output.test.ts +31 -31
- package/src/core/ai/context/providers/__tests__/datasource.test.ts +3 -3
- package/src/core/ai/context/providers/__tests__/tables.test.ts +3 -2
- package/src/core/ai/context/providers/__tests__/variable.test.ts +84 -63
- package/src/core/ai/tools/__tests__/edit-notebook-tool.test.ts +10 -9
- package/src/core/ai/tools/__tests__/run-cells-tool.test.ts +6 -6
- package/src/core/ai/tools/edit-notebook-tool.ts +3 -3
- package/src/core/cells/__tests__/add-missing-import.test.ts +3 -3
- package/src/core/cells/__tests__/apply-transaction.test.ts +279 -0
- package/src/core/cells/__tests__/cells.test.ts +198 -135
- package/src/core/cells/__tests__/document-changes.test.ts +575 -0
- package/src/core/cells/__tests__/document-roundtrip.test.ts +376 -0
- package/src/core/cells/__tests__/focus.test.ts +5 -4
- package/src/core/cells/__tests__/logs.test.ts +13 -12
- package/src/core/cells/__tests__/pending-delete-service.test.tsx +3 -3
- package/src/core/cells/__tests__/runs.test.ts +22 -21
- package/src/core/cells/__tests__/scrollCellIntoView.test.ts +8 -7
- package/src/core/cells/__tests__/session.test.ts +23 -22
- package/src/core/cells/cells.ts +29 -4
- package/src/core/cells/document-changes.ts +644 -0
- package/src/core/cells/ids.ts +5 -5
- package/src/core/cells/logs.ts +2 -2
- package/src/core/cells/runs.ts +6 -8
- package/src/core/codemirror/__tests__/format.test.ts +34 -36
- package/src/core/codemirror/__tests__/setup.test.ts +2 -2
- package/src/core/codemirror/cells/__tests__/extensions.test.ts +114 -0
- package/src/core/codemirror/cells/__tests__/traceback-decorations.test.ts +33 -32
- package/src/core/codemirror/cells/extensions.ts +66 -23
- package/src/core/codemirror/completion/__tests__/keymap.test.ts +15 -35
- package/src/core/codemirror/completion/keymap.ts +14 -4
- package/src/core/codemirror/copilot/__tests__/getCodes.test.ts +12 -13
- package/src/core/codemirror/language/__tests__/utils.test.ts +3 -3
- package/src/core/codemirror/language/embedded/__tests__/embedded-python.test.ts +7 -8
- package/src/core/codemirror/language/languages/python.ts +4 -0
- package/src/core/codemirror/lsp/__tests__/notebook-lsp.test.ts +4 -3
- package/src/core/codemirror/lsp/notebook-lsp.ts +28 -2
- package/src/core/codemirror/reactive-references/__tests__/analyzer.test.ts +7 -6
- package/src/core/codemirror/reactive-references/analyzer.ts +2 -2
- package/src/core/codemirror/rtc/loro/__tests__/sync.test.ts +52 -0
- package/src/core/codemirror/rtc/loro/sync.ts +1 -0
- package/src/core/datasets/__tests__/data-source.test.ts +5 -6
- package/src/core/datasets/state.ts +1 -1
- package/src/core/errors/__tests__/errors.test.ts +2 -1
- package/src/core/export/__tests__/hooks.test.ts +37 -36
- package/src/core/islands/bridge.ts +1 -0
- package/src/core/islands/main.ts +4 -7
- package/src/core/kernel/__tests__/handlers.test.ts +5 -4
- package/src/core/kernel/handlers.ts +7 -4
- package/src/core/network/DeferredRequestRegistry.ts +2 -2
- package/src/core/network/__tests__/CachingRequestRegistry.test.ts +9 -10
- package/src/core/network/__tests__/DeferredRequestRegistry.test.ts +4 -6
- package/src/core/network/requests-lazy.ts +1 -0
- package/src/core/network/requests-network.ts +9 -0
- package/src/core/network/requests-static.ts +1 -0
- package/src/core/network/requests-toasting.tsx +1 -0
- package/src/core/network/types.ts +5 -0
- package/src/core/static/__tests__/virtual-file-tracker.test.ts +8 -8
- package/src/core/static/virtual-file-tracker.ts +1 -1
- package/src/core/storage/__tests__/state.test.ts +31 -21
- package/src/core/storage/state.ts +1 -1
- package/src/core/variables/__tests__/state.test.ts +6 -6
- package/src/core/variables/types.ts +2 -2
- package/src/core/wasm/__tests__/state.test.ts +8 -8
- package/src/core/wasm/bridge.ts +1 -0
- package/src/core/websocket/useMarimoKernelConnection.tsx +31 -16
- package/src/css/app/fonts.css +6 -6
- package/src/css/md-tooltip.css +4 -39
- package/src/css/md.css +7 -0
- package/src/fonts/Fira_Mono/FiraMono-Bold.woff2 +0 -0
- package/src/fonts/Fira_Mono/FiraMono-Medium.woff2 +0 -0
- package/src/fonts/Fira_Mono/FiraMono-Regular.woff2 +0 -0
- package/src/fonts/Lora/Lora-VariableFont_wght.woff2 +0 -0
- package/src/fonts/PT_Sans/PTSans-Bold.woff2 +0 -0
- package/src/fonts/PT_Sans/PTSans-Regular.woff2 +0 -0
- package/src/plugins/core/RenderHTML.tsx +17 -0
- package/src/plugins/core/__test__/RenderHTML.test.ts +45 -0
- package/src/plugins/core/sanitize-html.ts +25 -18
- package/src/plugins/impl/DataTablePlugin.tsx +23 -2
- package/src/plugins/impl/SliderPlugin.tsx +1 -3
- package/src/plugins/impl/__tests__/SliderPlugin.test.tsx +120 -0
- package/src/plugins/impl/anywidget/model.ts +1 -2
- package/src/stories/cell.stories.tsx +8 -8
- package/src/stories/layout/vertical/one-column.stories.tsx +9 -8
- package/src/stories/log-viewer.stories.tsx +8 -8
- package/src/stories/variables.stories.tsx +2 -2
- package/src/utils/__tests__/download.test.tsx +21 -20
- package/src/utils/copy.ts +18 -5
- package/src/utils/createReducer.ts +26 -11
- package/src/utils/download.ts +4 -3
- package/src/utils/html-to-image.ts +6 -0
- package/src/utils/json/base64.ts +3 -3
- package/src/utils/traceback.ts +5 -3
- package/src/fonts/Fira_Mono/FiraMono-Bold.ttf +0 -0
- package/src/fonts/Fira_Mono/FiraMono-Medium.ttf +0 -0
- package/src/fonts/Fira_Mono/FiraMono-Regular.ttf +0 -0
- package/src/fonts/Lora/Lora-Italic-VariableFont_wght.ttf +0 -0
- package/src/fonts/Lora/Lora-VariableFont_wght.ttf +0 -0
- package/src/fonts/Lora/static/Lora-Bold.ttf +0 -0
- package/src/fonts/Lora/static/Lora-BoldItalic.ttf +0 -0
- package/src/fonts/Lora/static/Lora-Italic.ttf +0 -0
- package/src/fonts/Lora/static/Lora-Medium.ttf +0 -0
- package/src/fonts/Lora/static/Lora-MediumItalic.ttf +0 -0
- package/src/fonts/Lora/static/Lora-Regular.ttf +0 -0
- package/src/fonts/Lora/static/Lora-SemiBold.ttf +0 -0
- package/src/fonts/Lora/static/Lora-SemiBoldItalic.ttf +0 -0
- package/src/fonts/PT_Sans/PTSans-Bold.ttf +0 -0
- package/src/fonts/PT_Sans/PTSans-BoldItalic.ttf +0 -0
- package/src/fonts/PT_Sans/PTSans-Italic.ttf +0 -0
- package/src/fonts/PT_Sans/PTSans-Regular.ttf +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
import { beforeEach, describe, expect, it } from "vitest";
|
|
3
|
+
import { variableName } from "@/__tests__/branded";
|
|
3
4
|
import type { VariableName } from "../../variables/types";
|
|
4
5
|
import { exportedForTesting } from "../state";
|
|
5
6
|
import type { StorageEntry, StorageNamespace, StorageState } from "../types";
|
|
@@ -7,7 +8,7 @@ import type { StorageEntry, StorageNamespace, StorageState } from "../types";
|
|
|
7
8
|
const { initialState, reducer, createActions } = exportedForTesting;
|
|
8
9
|
|
|
9
10
|
function makeNamespace(
|
|
10
|
-
overrides: Partial<StorageNamespace> & { name:
|
|
11
|
+
overrides: Partial<StorageNamespace> & { name: VariableName },
|
|
11
12
|
): StorageNamespace {
|
|
12
13
|
return {
|
|
13
14
|
backendType: overrides.backendType ?? "obstore",
|
|
@@ -53,8 +54,14 @@ describe("storage state", () => {
|
|
|
53
54
|
|
|
54
55
|
describe("setNamespaces", () => {
|
|
55
56
|
it("should add namespaces from an empty state", () => {
|
|
56
|
-
const ns1 = makeNamespace({
|
|
57
|
-
|
|
57
|
+
const ns1 = makeNamespace({
|
|
58
|
+
name: variableName("my_s3"),
|
|
59
|
+
protocol: "s3",
|
|
60
|
+
});
|
|
61
|
+
const ns2 = makeNamespace({
|
|
62
|
+
name: variableName("my_gcs"),
|
|
63
|
+
protocol: "gcs",
|
|
64
|
+
});
|
|
58
65
|
|
|
59
66
|
actions.setNamespaces({ namespaces: [ns1, ns2] });
|
|
60
67
|
|
|
@@ -63,15 +70,18 @@ describe("storage state", () => {
|
|
|
63
70
|
|
|
64
71
|
it("should merge namespaces by name, replacing existing ones", () => {
|
|
65
72
|
const nsOld = makeNamespace({
|
|
66
|
-
name: "my_s3",
|
|
73
|
+
name: variableName("my_s3"),
|
|
67
74
|
protocol: "s3",
|
|
68
75
|
rootPath: "/old",
|
|
69
76
|
});
|
|
70
|
-
const nsOther = makeNamespace({
|
|
77
|
+
const nsOther = makeNamespace({
|
|
78
|
+
name: variableName("my_gcs"),
|
|
79
|
+
protocol: "gcs",
|
|
80
|
+
});
|
|
71
81
|
actions.setNamespaces({ namespaces: [nsOld, nsOther] });
|
|
72
82
|
|
|
73
83
|
const nsUpdated = makeNamespace({
|
|
74
|
-
name: "my_s3",
|
|
84
|
+
name: variableName("my_s3"),
|
|
75
85
|
protocol: "s3",
|
|
76
86
|
rootPath: "/new",
|
|
77
87
|
});
|
|
@@ -82,10 +92,10 @@ describe("storage state", () => {
|
|
|
82
92
|
});
|
|
83
93
|
|
|
84
94
|
it("should add new namespaces alongside existing ones", () => {
|
|
85
|
-
const ns1 = makeNamespace({ name: "ns1" });
|
|
95
|
+
const ns1 = makeNamespace({ name: variableName("ns1") });
|
|
86
96
|
actions.setNamespaces({ namespaces: [ns1] });
|
|
87
97
|
|
|
88
|
-
const ns2 = makeNamespace({ name: "ns2" });
|
|
98
|
+
const ns2 = makeNamespace({ name: variableName("ns2") });
|
|
89
99
|
actions.setNamespaces({ namespaces: [ns2] });
|
|
90
100
|
|
|
91
101
|
expect(state.namespaces).toHaveLength(2);
|
|
@@ -101,7 +111,7 @@ describe("storage state", () => {
|
|
|
101
111
|
});
|
|
102
112
|
|
|
103
113
|
actions.setNamespaces({
|
|
104
|
-
namespaces: [makeNamespace({ name: "ns" })],
|
|
114
|
+
namespaces: [makeNamespace({ name: variableName("ns") })],
|
|
105
115
|
});
|
|
106
116
|
|
|
107
117
|
expect(state.entriesByPath.get("ns::")).toEqual([entry]);
|
|
@@ -205,7 +215,7 @@ describe("storage state", () => {
|
|
|
205
215
|
});
|
|
206
216
|
|
|
207
217
|
it("should not affect namespaces", () => {
|
|
208
|
-
const ns = makeNamespace({ name: "ns" });
|
|
218
|
+
const ns = makeNamespace({ name: variableName("ns") });
|
|
209
219
|
actions.setNamespaces({ namespaces: [ns] });
|
|
210
220
|
|
|
211
221
|
actions.setEntries({
|
|
@@ -263,7 +273,7 @@ describe("storage state", () => {
|
|
|
263
273
|
});
|
|
264
274
|
|
|
265
275
|
it("should not affect namespaces", () => {
|
|
266
|
-
const ns = makeNamespace({ name: "my_s3" });
|
|
276
|
+
const ns = makeNamespace({ name: variableName("my_s3") });
|
|
267
277
|
actions.setNamespaces({ namespaces: [ns] });
|
|
268
278
|
actions.setEntries({
|
|
269
279
|
namespace: "my_s3",
|
|
@@ -293,31 +303,31 @@ describe("storage state", () => {
|
|
|
293
303
|
|
|
294
304
|
describe("filterFromVariables", () => {
|
|
295
305
|
it("should keep namespaces whose variable is still in scope", () => {
|
|
296
|
-
const ns1 = makeNamespace({ name: "var_a" });
|
|
297
|
-
const ns2 = makeNamespace({ name: "var_b" });
|
|
306
|
+
const ns1 = makeNamespace({ name: variableName("var_a") });
|
|
307
|
+
const ns2 = makeNamespace({ name: variableName("var_b") });
|
|
298
308
|
actions.setNamespaces({ namespaces: [ns1, ns2] });
|
|
299
309
|
|
|
300
310
|
actions.filterFromVariables([
|
|
301
|
-
"var_a"
|
|
302
|
-
"var_b"
|
|
311
|
+
variableName("var_a"),
|
|
312
|
+
variableName("var_b"),
|
|
303
313
|
]);
|
|
304
314
|
|
|
305
315
|
expect(state.namespaces).toEqual([ns1, ns2]);
|
|
306
316
|
});
|
|
307
317
|
|
|
308
318
|
it("should remove namespaces whose variable is no longer in scope", () => {
|
|
309
|
-
const ns1 = makeNamespace({ name: "var_a" });
|
|
310
|
-
const ns2 = makeNamespace({ name: "var_b" });
|
|
319
|
+
const ns1 = makeNamespace({ name: variableName("var_a") });
|
|
320
|
+
const ns2 = makeNamespace({ name: variableName("var_b") });
|
|
311
321
|
actions.setNamespaces({ namespaces: [ns1, ns2] });
|
|
312
322
|
|
|
313
|
-
actions.filterFromVariables(["var_a"
|
|
323
|
+
actions.filterFromVariables([variableName("var_a")]);
|
|
314
324
|
|
|
315
325
|
expect(state.namespaces).toEqual([ns1]);
|
|
316
326
|
});
|
|
317
327
|
|
|
318
328
|
it("should remove all named namespaces when given an empty variable list", () => {
|
|
319
|
-
const ns1 = makeNamespace({ name: "var_a" });
|
|
320
|
-
const ns2 = makeNamespace({ name: "var_b" });
|
|
329
|
+
const ns1 = makeNamespace({ name: variableName("var_a") });
|
|
330
|
+
const ns2 = makeNamespace({ name: variableName("var_b") });
|
|
321
331
|
actions.setNamespaces({ namespaces: [ns1, ns2] });
|
|
322
332
|
|
|
323
333
|
actions.filterFromVariables([]);
|
|
@@ -326,7 +336,7 @@ describe("storage state", () => {
|
|
|
326
336
|
});
|
|
327
337
|
|
|
328
338
|
it("should not affect entriesByPath", () => {
|
|
329
|
-
const ns = makeNamespace({ name: "ns" });
|
|
339
|
+
const ns = makeNamespace({ name: variableName("ns") });
|
|
330
340
|
actions.setNamespaces({ namespaces: [ns] });
|
|
331
341
|
|
|
332
342
|
const entry = makeEntry({ path: "file.txt" });
|
|
@@ -71,7 +71,7 @@ const {
|
|
|
71
71
|
const names = new Set(variableNames);
|
|
72
72
|
// Filter out namespaces whose backing variable is no longer in scope
|
|
73
73
|
const namespaces = state.namespaces.filter((ns) => {
|
|
74
|
-
return names.has(ns.name
|
|
74
|
+
return names.has(ns.name);
|
|
75
75
|
});
|
|
76
76
|
return { ...state, namespaces };
|
|
77
77
|
},
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
import { beforeEach, describe, expect, it } from "vitest";
|
|
3
|
-
import
|
|
3
|
+
import { cellId, variableName } from "@/__tests__/branded";
|
|
4
4
|
import { exportedForTesting } from "../state";
|
|
5
|
-
import type {
|
|
5
|
+
import type { Variables } from "../types";
|
|
6
6
|
|
|
7
7
|
const { initialState, reducer, createActions } = exportedForTesting;
|
|
8
8
|
|
|
9
9
|
const CellIds = {
|
|
10
|
-
a: "a"
|
|
11
|
-
b: "b"
|
|
10
|
+
a: cellId("a"),
|
|
11
|
+
b: cellId("b"),
|
|
12
12
|
};
|
|
13
13
|
|
|
14
14
|
const Names = {
|
|
15
|
-
x: "x"
|
|
16
|
-
y: "y"
|
|
15
|
+
x: variableName("x"),
|
|
16
|
+
y: variableName("y"),
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
describe("cell reducer", () => {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
|
-
import type {
|
|
3
|
+
import type { components } from "@marimo-team/marimo-api";
|
|
4
4
|
import type { CellId } from "../cells/ids";
|
|
5
5
|
|
|
6
|
-
export type VariableName =
|
|
6
|
+
export type VariableName = components["schemas"]["VariableName"];
|
|
7
7
|
|
|
8
8
|
export interface Variable {
|
|
9
9
|
name: VariableName;
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
import { createRef } from "react";
|
|
4
4
|
import { describe, expect, it } from "vitest";
|
|
5
|
+
import { cellId } from "@/__tests__/branded";
|
|
5
6
|
import type { NotebookState } from "@/core/cells/cells";
|
|
6
7
|
import { initialNotebookState, notebookAtom } from "@/core/cells/cells";
|
|
7
|
-
import type { CellId } from "@/core/cells/ids";
|
|
8
8
|
import { createCell, createCellRuntimeState } from "@/core/cells/types";
|
|
9
9
|
import type { OutputMessage } from "@/core/kernel/messages";
|
|
10
10
|
import { store } from "@/core/state/jotai";
|
|
@@ -17,17 +17,17 @@ describe("hasAnyOutputAtom", () => {
|
|
|
17
17
|
): NotebookState => ({
|
|
18
18
|
...initialNotebookState(),
|
|
19
19
|
cellIds: new MultiColumn([
|
|
20
|
-
CollapsibleTree.from(outputs.map((_, i) => `${i}`
|
|
20
|
+
CollapsibleTree.from(outputs.map((_, i) => cellId(`${i}`))),
|
|
21
21
|
]),
|
|
22
22
|
cellData: Object.fromEntries(
|
|
23
23
|
outputs.map((_, i) => [
|
|
24
|
-
`${i}`
|
|
25
|
-
createCell({ id: `${i}`
|
|
24
|
+
cellId(`${i}`),
|
|
25
|
+
createCell({ id: cellId(`${i}`) }),
|
|
26
26
|
]),
|
|
27
27
|
),
|
|
28
28
|
cellRuntime: Object.fromEntries(
|
|
29
29
|
outputs.map((output, i) => [
|
|
30
|
-
`${i}
|
|
30
|
+
`${i}`,
|
|
31
31
|
createCellRuntimeState({
|
|
32
32
|
output,
|
|
33
33
|
status: "queued",
|
|
@@ -36,7 +36,7 @@ describe("hasAnyOutputAtom", () => {
|
|
|
36
36
|
]),
|
|
37
37
|
),
|
|
38
38
|
cellHandles: Object.fromEntries(
|
|
39
|
-
outputs.map((_, i) => [`${i}`
|
|
39
|
+
outputs.map((_, i) => [cellId(`${i}`), createRef()]),
|
|
40
40
|
),
|
|
41
41
|
});
|
|
42
42
|
|
|
@@ -90,8 +90,8 @@ describe("hasAnyOutputAtom", () => {
|
|
|
90
90
|
|
|
91
91
|
it("should return true when all outputs are idle", () => {
|
|
92
92
|
const notebookState = createNotebookState([null, null]);
|
|
93
|
-
const cellId0 = "0"
|
|
94
|
-
const cellId1 = "1"
|
|
93
|
+
const cellId0 = cellId("0");
|
|
94
|
+
const cellId1 = cellId("1");
|
|
95
95
|
// Some idle cell, so returns false
|
|
96
96
|
store.set(notebookAtom, {
|
|
97
97
|
...notebookState,
|
package/src/core/wasm/bridge.ts
CHANGED
|
@@ -549,6 +549,7 @@ export class PyodideBridge implements RunRequests, EditRequests {
|
|
|
549
549
|
};
|
|
550
550
|
|
|
551
551
|
syncCellIds = () => Promise.resolve(null);
|
|
552
|
+
sendDocumentTransaction = () => Promise.resolve(null);
|
|
552
553
|
|
|
553
554
|
addPackage: EditRequests["addPackage"] = async (request) => {
|
|
554
555
|
return this.rpc.proxy.request.addPackage(request);
|
|
@@ -5,8 +5,12 @@ import { useRef } from "react";
|
|
|
5
5
|
import { useErrorBoundary } from "react-error-boundary";
|
|
6
6
|
import { toast } from "@/components/ui/use-toast";
|
|
7
7
|
import { getNotebook, useCellActions } from "@/core/cells/cells";
|
|
8
|
+
import { applyTransactionChanges } from "@/core/cells/document-changes";
|
|
8
9
|
import { AUTOCOMPLETER } from "@/core/codemirror/completion/Autocompleter";
|
|
9
|
-
import type {
|
|
10
|
+
import type {
|
|
11
|
+
NotificationMessageData,
|
|
12
|
+
NotificationPayload,
|
|
13
|
+
} from "@/core/kernel/messages";
|
|
10
14
|
import { useConnectionTransport } from "@/core/websocket/useWebSocket";
|
|
11
15
|
import { renderHTML } from "@/plugins/core/RenderHTML";
|
|
12
16
|
import {
|
|
@@ -24,7 +28,7 @@ import { Logger } from "@/utils/Logger";
|
|
|
24
28
|
import { reloadSafe } from "@/utils/reload-safe";
|
|
25
29
|
import { useAlertActions } from "../alerts/state";
|
|
26
30
|
import { cacheInfoAtom } from "../cache/requests";
|
|
27
|
-
import {
|
|
31
|
+
import { SCRATCH_CELL_ID } from "../cells/ids";
|
|
28
32
|
import { useRunsActions } from "../cells/runs";
|
|
29
33
|
import { focusAndScrollCellOutputIntoView } from "../cells/scrollCellIntoView";
|
|
30
34
|
import type { CellData } from "../cells/types";
|
|
@@ -93,6 +97,17 @@ export function useMarimoKernelConnection(opts: {
|
|
|
93
97
|
const { showBoundary } = useErrorBoundary();
|
|
94
98
|
|
|
95
99
|
const { handleCellMessage, setCellCodes, setCellIds } = useCellActions();
|
|
100
|
+
const actionsWithoutMiddleware = useCellActions({ skipMiddleware: true });
|
|
101
|
+
|
|
102
|
+
const handleDocumentTransaction = (
|
|
103
|
+
transaction: NotificationMessageData<"notebook-document-transaction">["transaction"],
|
|
104
|
+
) => {
|
|
105
|
+
applyTransactionChanges(
|
|
106
|
+
transaction.changes,
|
|
107
|
+
actionsWithoutMiddleware,
|
|
108
|
+
() => getNotebook().cellIds.inOrderIds,
|
|
109
|
+
);
|
|
110
|
+
};
|
|
96
111
|
const { addCellNotification } = useRunsActions();
|
|
97
112
|
const setKernelState = useSetAtom(kernelStateAtom);
|
|
98
113
|
const setAppConfig = useSetAppConfig();
|
|
@@ -153,7 +168,7 @@ export function useMarimoKernelConnection(opts: {
|
|
|
153
168
|
if (uiElement) {
|
|
154
169
|
const buffers = safeExtractSetUIElementMessageBuffers(msg.data);
|
|
155
170
|
UI_ELEMENT_REGISTRY.broadcastMessage(
|
|
156
|
-
uiElement
|
|
171
|
+
uiElement,
|
|
157
172
|
msg.data.message,
|
|
158
173
|
buffers,
|
|
159
174
|
);
|
|
@@ -173,14 +188,11 @@ export function useMarimoKernelConnection(opts: {
|
|
|
173
188
|
AUTOCOMPLETER.resolve(msg.data.completion_id as RequestId, msg.data);
|
|
174
189
|
return;
|
|
175
190
|
case "function-call-result":
|
|
176
|
-
FUNCTIONS_REGISTRY.resolve(
|
|
177
|
-
msg.data.function_call_id as RequestId,
|
|
178
|
-
msg.data,
|
|
179
|
-
);
|
|
191
|
+
FUNCTIONS_REGISTRY.resolve(msg.data.function_call_id, msg.data);
|
|
180
192
|
return;
|
|
181
193
|
case "cell-op": {
|
|
182
194
|
handleCellNotificationeration(msg.data, handleCellMessage);
|
|
183
|
-
const cellData = getNotebook().cellData[msg.data.cell_id
|
|
195
|
+
const cellData = getNotebook().cellData[msg.data.cell_id];
|
|
184
196
|
if (!cellData) {
|
|
185
197
|
return;
|
|
186
198
|
}
|
|
@@ -195,8 +207,8 @@ export function useMarimoKernelConnection(opts: {
|
|
|
195
207
|
setVariables(
|
|
196
208
|
msg.data.variables.map((v) => ({
|
|
197
209
|
name: v.name as VariableName,
|
|
198
|
-
declaredBy: v.declared_by
|
|
199
|
-
usedBy: v.used_by
|
|
210
|
+
declaredBy: v.declared_by,
|
|
211
|
+
usedBy: v.used_by,
|
|
200
212
|
})),
|
|
201
213
|
);
|
|
202
214
|
filterDatasetsFromVariables(
|
|
@@ -271,16 +283,16 @@ export function useMarimoKernelConnection(opts: {
|
|
|
271
283
|
addColumnPreview(msg.data);
|
|
272
284
|
return;
|
|
273
285
|
case "sql-table-preview":
|
|
274
|
-
PreviewSQLTable.resolve(msg.data.request_id
|
|
286
|
+
PreviewSQLTable.resolve(msg.data.request_id, msg.data);
|
|
275
287
|
return;
|
|
276
288
|
case "sql-table-list-preview":
|
|
277
|
-
PreviewSQLTableList.resolve(msg.data.request_id
|
|
289
|
+
PreviewSQLTableList.resolve(msg.data.request_id, msg.data);
|
|
278
290
|
return;
|
|
279
291
|
case "validate-sql-result":
|
|
280
292
|
ValidateSQL.resolve(msg.data.request_id as RequestId, msg.data);
|
|
281
293
|
return;
|
|
282
294
|
case "secret-keys-result":
|
|
283
|
-
SECRETS_REGISTRY.resolve(msg.data.request_id
|
|
295
|
+
SECRETS_REGISTRY.resolve(msg.data.request_id, msg.data);
|
|
284
296
|
return;
|
|
285
297
|
case "cache-info":
|
|
286
298
|
setCacheInfo(msg.data);
|
|
@@ -310,19 +322,22 @@ export function useMarimoKernelConnection(opts: {
|
|
|
310
322
|
return;
|
|
311
323
|
|
|
312
324
|
case "focus-cell":
|
|
313
|
-
focusAndScrollCellOutputIntoView(msg.data.cell_id
|
|
325
|
+
focusAndScrollCellOutputIntoView(msg.data.cell_id);
|
|
314
326
|
return;
|
|
315
327
|
case "update-cell-codes":
|
|
316
328
|
setCellCodes({
|
|
317
329
|
codes: msg.data.codes,
|
|
318
|
-
ids: msg.data.cell_ids
|
|
330
|
+
ids: msg.data.cell_ids,
|
|
319
331
|
codeIsStale: msg.data.code_is_stale,
|
|
320
332
|
names: msg.data.names,
|
|
321
333
|
configs: msg.data.configs,
|
|
322
334
|
});
|
|
323
335
|
return;
|
|
324
336
|
case "update-cell-ids":
|
|
325
|
-
setCellIds({ cellIds: msg.data.cell_ids
|
|
337
|
+
setCellIds({ cellIds: msg.data.cell_ids });
|
|
338
|
+
return;
|
|
339
|
+
case "notebook-document-transaction":
|
|
340
|
+
handleDocumentTransaction(msg.data.transaction);
|
|
326
341
|
return;
|
|
327
342
|
default:
|
|
328
343
|
logNever(msg.data);
|
package/src/css/app/fonts.css
CHANGED
|
@@ -6,7 +6,7 @@ see frontend/src/core/static/download-html.ts */
|
|
|
6
6
|
font-style: normal;
|
|
7
7
|
font-weight: 400;
|
|
8
8
|
font-display: block;
|
|
9
|
-
src: url("../../fonts/Lora/Lora-VariableFont_wght.
|
|
9
|
+
src: url("../../fonts/Lora/Lora-VariableFont_wght.woff2") format("woff2");
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
@font-face {
|
|
@@ -14,7 +14,7 @@ see frontend/src/core/static/download-html.ts */
|
|
|
14
14
|
font-style: normal;
|
|
15
15
|
font-weight: 400;
|
|
16
16
|
font-display: block;
|
|
17
|
-
src: url("../../fonts/PT_Sans/PTSans-Regular.
|
|
17
|
+
src: url("../../fonts/PT_Sans/PTSans-Regular.woff2") format("woff2");
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
@font-face {
|
|
@@ -22,7 +22,7 @@ see frontend/src/core/static/download-html.ts */
|
|
|
22
22
|
font-style: normal;
|
|
23
23
|
font-weight: 700;
|
|
24
24
|
font-display: block;
|
|
25
|
-
src: url("../../fonts/PT_Sans/PTSans-Bold.
|
|
25
|
+
src: url("../../fonts/PT_Sans/PTSans-Bold.woff2") format("woff2");
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
@font-face {
|
|
@@ -30,7 +30,7 @@ see frontend/src/core/static/download-html.ts */
|
|
|
30
30
|
font-style: normal;
|
|
31
31
|
font-weight: 400;
|
|
32
32
|
font-display: block;
|
|
33
|
-
src: url("../../fonts/Fira_Mono/FiraMono-Regular.
|
|
33
|
+
src: url("../../fonts/Fira_Mono/FiraMono-Regular.woff2") format("woff2");
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
@font-face {
|
|
@@ -38,7 +38,7 @@ see frontend/src/core/static/download-html.ts */
|
|
|
38
38
|
font-style: normal;
|
|
39
39
|
font-weight: 500;
|
|
40
40
|
font-display: block;
|
|
41
|
-
src: url("../../fonts/Fira_Mono/FiraMono-Medium.
|
|
41
|
+
src: url("../../fonts/Fira_Mono/FiraMono-Medium.woff2") format("woff2");
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
@font-face {
|
|
@@ -46,5 +46,5 @@ see frontend/src/core/static/download-html.ts */
|
|
|
46
46
|
font-style: normal;
|
|
47
47
|
font-weight: 700;
|
|
48
48
|
font-display: block;
|
|
49
|
-
src: url("../../fonts/Fira_Mono/FiraMono-Bold.
|
|
49
|
+
src: url("../../fonts/Fira_Mono/FiraMono-Bold.woff2") format("woff2");
|
|
50
50
|
}
|
package/src/css/md-tooltip.css
CHANGED
|
@@ -3,50 +3,15 @@
|
|
|
3
3
|
/*
|
|
4
4
|
This allows you to create a basic tooltip using the data-tooltip attribute
|
|
5
5
|
e.g. <span data-tooltip="Hello, World!">Hover me</span>
|
|
6
|
+
|
|
7
|
+
The tooltip content is rendered via the React Tooltip component (Radix UI portal),
|
|
8
|
+
which prevents clipping inside containers with overflow:hidden.
|
|
9
|
+
See: RenderHTML.tsx -> wrapTooltipTargets
|
|
6
10
|
*/
|
|
7
11
|
|
|
8
12
|
.markdown {
|
|
9
13
|
[data-tooltip] {
|
|
10
|
-
position: relative;
|
|
11
14
|
cursor: pointer;
|
|
12
15
|
text-decoration: underline dotted;
|
|
13
16
|
}
|
|
14
|
-
|
|
15
|
-
[data-tooltip]::before,
|
|
16
|
-
[data-tooltip]::after {
|
|
17
|
-
visibility: hidden;
|
|
18
|
-
opacity: 0;
|
|
19
|
-
pointer-events: none;
|
|
20
|
-
transition: all 0.2s ease;
|
|
21
|
-
position: absolute;
|
|
22
|
-
z-index: 1000;
|
|
23
|
-
left: 50%;
|
|
24
|
-
transform: translateX(-50%);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
[data-tooltip]::before {
|
|
28
|
-
content: attr(data-tooltip);
|
|
29
|
-
bottom: calc(100% + 10px);
|
|
30
|
-
padding: 5px 10px;
|
|
31
|
-
width: max-content;
|
|
32
|
-
max-width: 300px;
|
|
33
|
-
border-radius: 6px;
|
|
34
|
-
background-color: ;
|
|
35
|
-
text-align: center;
|
|
36
|
-
line-height: 1.4;
|
|
37
|
-
white-space: pre-wrap;
|
|
38
|
-
|
|
39
|
-
@apply bg-background text-foreground shadow-md border text-base;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
[data-tooltip]:hover::before,
|
|
43
|
-
[data-tooltip]:hover::after {
|
|
44
|
-
visibility: visible;
|
|
45
|
-
opacity: 1;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
[data-tooltip]:hover::before {
|
|
49
|
-
/* stylelint-disable-next-line unit-allowed-list */
|
|
50
|
-
transform: translateX(-50%) translateY(10px);
|
|
51
|
-
}
|
|
52
17
|
}
|
package/src/css/md.css
CHANGED
|
@@ -374,6 +374,13 @@ button .prose.prose {
|
|
|
374
374
|
@apply p-4 pt-0;
|
|
375
375
|
}
|
|
376
376
|
|
|
377
|
+
/* Restore proper list indentation inside details blocks.
|
|
378
|
+
The p-4 above overrides prose's padding-inline-start for bullet space.
|
|
379
|
+
This ensures bullets render correctly with list-style-position: outside. */
|
|
380
|
+
.markdown details > :is(ul, ol) {
|
|
381
|
+
padding-inline-start: 2.5rem;
|
|
382
|
+
}
|
|
383
|
+
|
|
377
384
|
.markdown .codehilite {
|
|
378
385
|
background-color: var(--slate-2);
|
|
379
386
|
border-radius: 4px;
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -14,6 +14,7 @@ import React, {
|
|
|
14
14
|
} from "react";
|
|
15
15
|
import { CopyClipboardIcon } from "@/components/icons/copy-icon";
|
|
16
16
|
import { QueryParamPreservingLink } from "@/components/ui/query-param-preserving-link";
|
|
17
|
+
import { Tooltip } from "@/components/ui/tooltip";
|
|
17
18
|
import { DocHoverTarget } from "@/core/documentation/DocHoverTarget";
|
|
18
19
|
import { sanitizeHtml, useSanitizeHtml } from "./sanitize";
|
|
19
20
|
|
|
@@ -160,6 +161,21 @@ const wrapDocHoverTargets: TransformFn = (
|
|
|
160
161
|
}
|
|
161
162
|
};
|
|
162
163
|
|
|
164
|
+
// Wrap elements with data-tooltip attribute in a Tooltip component.
|
|
165
|
+
// This renders the tooltip in a portal (top layer), fixing clipping inside
|
|
166
|
+
// containers with overflow:hidden (e.g. grid cells).
|
|
167
|
+
const wrapTooltipTargets: TransformFn = (
|
|
168
|
+
reactNode: ReactNode,
|
|
169
|
+
domNode: DOMNode,
|
|
170
|
+
): JSX.Element | undefined => {
|
|
171
|
+
if (domNode instanceof Element && domNode.attribs?.["data-tooltip"]) {
|
|
172
|
+
const tooltipContent = domNode.attribs["data-tooltip"];
|
|
173
|
+
return (
|
|
174
|
+
<Tooltip content={tooltipContent}>{reactNode as JSX.Element}</Tooltip>
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
|
|
163
179
|
const CopyableCode = ({ children }: { children: ReactNode }) => {
|
|
164
180
|
const ref = useRef<HTMLDivElement>(null);
|
|
165
181
|
return (
|
|
@@ -239,6 +255,7 @@ function parseHtml({
|
|
|
239
255
|
addCopyButtonToCodehilite,
|
|
240
256
|
preserveQueryParamsInAnchorLinks,
|
|
241
257
|
wrapDocHoverTargets,
|
|
258
|
+
wrapTooltipTargets,
|
|
242
259
|
removeWrappingBodyTags,
|
|
243
260
|
removeWrappingHtmlTags,
|
|
244
261
|
];
|
|
@@ -197,6 +197,51 @@ describe("parseHtml", () => {
|
|
|
197
197
|
});
|
|
198
198
|
});
|
|
199
199
|
|
|
200
|
+
describe("wrapTooltipTargets", () => {
|
|
201
|
+
test("data-tooltip wraps element in Tooltip component", () => {
|
|
202
|
+
const html = '<span data-tooltip="Hello world">Hover me</span>';
|
|
203
|
+
expect(parseHtml({ html })).toMatchInlineSnapshot(`
|
|
204
|
+
<Tooltip
|
|
205
|
+
content="Hello world"
|
|
206
|
+
>
|
|
207
|
+
<span
|
|
208
|
+
data-tooltip="Hello world"
|
|
209
|
+
>
|
|
210
|
+
Hover me
|
|
211
|
+
</span>
|
|
212
|
+
</Tooltip>
|
|
213
|
+
`);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
test("element without data-tooltip is not wrapped", () => {
|
|
217
|
+
const html = "<span>No tooltip</span>";
|
|
218
|
+
expect(parseHtml({ html })).toMatchInlineSnapshot(`
|
|
219
|
+
<span>
|
|
220
|
+
No tooltip
|
|
221
|
+
</span>
|
|
222
|
+
`);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
test("data-tooltip on nested element wraps only that element", () => {
|
|
226
|
+
const html = '<p>Outer <span data-tooltip="tip">inner</span> text</p>';
|
|
227
|
+
expect(parseHtml({ html })).toMatchInlineSnapshot(`
|
|
228
|
+
<p>
|
|
229
|
+
Outer
|
|
230
|
+
<Tooltip
|
|
231
|
+
content="tip"
|
|
232
|
+
>
|
|
233
|
+
<span
|
|
234
|
+
data-tooltip="tip"
|
|
235
|
+
>
|
|
236
|
+
inner
|
|
237
|
+
</span>
|
|
238
|
+
</Tooltip>
|
|
239
|
+
text
|
|
240
|
+
</p>
|
|
241
|
+
`);
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
|
|
200
245
|
describe("parseHtml with < nad >", () => {
|
|
201
246
|
const html =
|
|
202
247
|
'thread <unnamed> panicked at "assertion failed: `(left == right)`"';
|
|
@@ -2,28 +2,35 @@
|
|
|
2
2
|
import DOMPurify, { type Config } from "dompurify";
|
|
3
3
|
|
|
4
4
|
// preserve target=_blank https://github.com/cure53/DOMPurify/issues/317#issuecomment-912474068
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
// Guard for non-browser environments (e.g. Node.js in the marimo-lsp extension)
|
|
6
|
+
// where `document` is not available.
|
|
7
|
+
if (typeof document !== "undefined") {
|
|
8
|
+
const TEMPORARY_ATTRIBUTE = "data-temp-href-target";
|
|
9
|
+
DOMPurify.addHook("beforeSanitizeAttributes", (node) => {
|
|
10
|
+
if (node.tagName === "A") {
|
|
11
|
+
if (!node.hasAttribute("target")) {
|
|
12
|
+
node.setAttribute("target", "_self");
|
|
13
|
+
}
|
|
11
14
|
|
|
12
|
-
|
|
13
|
-
|
|
15
|
+
if (node.hasAttribute("target")) {
|
|
16
|
+
node.setAttribute(
|
|
17
|
+
TEMPORARY_ATTRIBUTE,
|
|
18
|
+
node.getAttribute("target") || "",
|
|
19
|
+
);
|
|
20
|
+
}
|
|
14
21
|
}
|
|
15
|
-
}
|
|
16
|
-
});
|
|
22
|
+
});
|
|
17
23
|
|
|
18
|
-
DOMPurify.addHook("afterSanitizeAttributes", (node) => {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
DOMPurify.addHook("afterSanitizeAttributes", (node) => {
|
|
25
|
+
if (node.tagName === "A" && node.hasAttribute(TEMPORARY_ATTRIBUTE)) {
|
|
26
|
+
node.setAttribute("target", node.getAttribute(TEMPORARY_ATTRIBUTE) || "");
|
|
27
|
+
node.removeAttribute(TEMPORARY_ATTRIBUTE);
|
|
28
|
+
if (node.getAttribute("target") === "_blank") {
|
|
29
|
+
node.setAttribute("rel", "noopener noreferrer");
|
|
30
|
+
}
|
|
24
31
|
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
27
34
|
|
|
28
35
|
/**
|
|
29
36
|
* This removes script tags, form tags, iframe tags, and other potentially dangerous tags
|