@marimo-team/frontend 0.22.1-dev33 → 0.22.1-dev38
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/assets/{JsonOutput-CNt4LxkV.js → JsonOutput-DboWEw2n.js} +2 -2
- package/dist/assets/{add-connection-dialog-Ca1Tc12W.js → add-connection-dialog-DiHC8_uD.js} +1 -1
- package/dist/assets/{agent-panel-BYAJ_EOf.js → agent-panel-CWWFNdAZ.js} +1 -1
- package/dist/assets/{cell-editor-CSxVMYfU.js → cell-editor-CKnV9MwH.js} +2 -2
- package/dist/assets/{column-preview-B92BXVH1.js → column-preview-BvDPfcdF.js} +1 -1
- package/dist/assets/{command-palette-BHXQKZ5s.js → command-palette-CPRmmNjA.js} +1 -1
- package/dist/assets/common-CRBlPqv5.js +1 -0
- package/dist/assets/{dependency-graph-panel-CABb99dr.js → dependency-graph-panel-BBmN-Vc7.js} +1 -1
- package/dist/assets/{edit-page-DtYaxOp-.js → edit-page-B-kCXJQD.js} +4 -4
- package/dist/assets/{file-explorer-panel-DJgn_eST.js → file-explorer-panel-DyDct4o3.js} +1 -1
- package/dist/assets/{form-BFjzbGu5.js → form-Byef3KYr.js} +1 -1
- package/dist/assets/{hooks-C2LN7xdC.js → hooks-BoxLBXGI.js} +1 -1
- package/dist/assets/index-B1HqiNNr.js +42 -0
- package/dist/assets/{index-BaQAJwyb.css → index-BkdonYlq.css} +1 -1
- package/dist/assets/{layout-Cb75LpRE.js → layout-BjQtqcnj.js} +2 -2
- package/dist/assets/{panels-C-PHvQEf.js → panels-aBXT7_tR.js} +1 -1
- package/dist/assets/{run-page-BZ-VoP12.js → run-page-yA3m6QEA.js} +1 -1
- package/dist/assets/{scratchpad-panel-CCcUfWUz.js → scratchpad-panel-Cul3iUG0.js} +1 -1
- package/dist/assets/{session-panel-DaLr35Wc.js → session-panel-CzPIEKo8.js} +1 -1
- package/dist/assets/{snippets-panel-qn7otI-U.js → snippets-panel-DQgE3W8I.js} +1 -1
- package/dist/assets/{state-q7CKuEm6.js → state-C79RsVoe.js} +1 -1
- package/dist/assets/useNotebookActions-A-2lB6Y1.js +1 -0
- package/dist/index.html +6 -6
- package/package.json +1 -1
- package/src/components/data-table/data-table.tsx +12 -12
- package/src/components/editor/actions/pair-with-agent-modal.tsx +142 -0
- package/src/components/editor/actions/useNotebookActions.tsx +10 -0
- package/src/components/editor/cell/code/cell-editor.tsx +1 -1
- package/src/components/editor/chrome/panels/snippets-panel.tsx +1 -1
- package/src/components/editor/links/cell-link-list.tsx +1 -1
- package/src/components/editor/navigation/multi-cell-action-toolbar.tsx +2 -3
- package/src/components/editor/navigation/navigation.ts +2 -2
- package/src/components/editor/output/console/ConsoleOutput.tsx +1 -1
- package/src/plugins/impl/plotly/PlotlyPlugin.tsx +62 -44
- package/src/plugins/impl/plotly/__tests__/PlotlyPlugin.test.tsx +114 -0
- package/src/plugins/impl/plotly/__tests__/selection.test.ts +158 -196
- package/src/plugins/impl/plotly/selection.ts +274 -56
- package/src/utils/mime-types.ts +1 -1
- package/dist/assets/common-B5GX57h6.js +0 -1
- package/dist/assets/index-BG82ditz.js +0 -35
- package/dist/assets/useNotebookActions-DB5vGtvM.js +0 -1
|
@@ -1,237 +1,199 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
3
|
import type * as Plotly from "plotly.js";
|
|
4
|
-
import { describe, expect, it } from "vitest";
|
|
4
|
+
import { describe, expect, it, vi } from "vitest";
|
|
5
5
|
import {
|
|
6
|
-
extractClickSelection,
|
|
7
6
|
extractIndices,
|
|
8
7
|
extractPoints,
|
|
8
|
+
hasPureLineTrace,
|
|
9
|
+
lineSelectionButtons,
|
|
10
|
+
type ModeBarButton,
|
|
11
|
+
mergeModeBarButtonsToAdd,
|
|
12
|
+
shouldHandleClickSelection,
|
|
9
13
|
} from "../selection";
|
|
10
14
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
type: string;
|
|
14
|
-
hovertemplate?: string | string[];
|
|
15
|
-
};
|
|
16
|
-
[key: string]: unknown;
|
|
15
|
+
function createTrace(trace: Partial<Plotly.PlotData>): Plotly.Data {
|
|
16
|
+
return trace as unknown as Plotly.Data;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
function
|
|
20
|
-
return
|
|
19
|
+
function createPlotDatum<T extends object>(overrides: T): Plotly.PlotDatum & T {
|
|
20
|
+
return overrides as unknown as Plotly.PlotDatum & T;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
makePoint({
|
|
33
|
-
pointIndex: 2,
|
|
34
|
-
pointNumber: 99,
|
|
35
|
-
data: { type: "scatter" },
|
|
36
|
-
}),
|
|
37
|
-
makePoint({
|
|
38
|
-
pointNumber: 4,
|
|
39
|
-
data: { type: "scattergl" },
|
|
40
|
-
}),
|
|
41
|
-
makePoint({
|
|
42
|
-
data: { type: "heatmap" },
|
|
43
|
-
}),
|
|
44
|
-
];
|
|
23
|
+
describe("hasPureLineTrace", () => {
|
|
24
|
+
it("detects scatter and scattergl traces that are pure lines", () => {
|
|
25
|
+
expect(
|
|
26
|
+
hasPureLineTrace([
|
|
27
|
+
createTrace({ type: "scatter", mode: "lines" }),
|
|
28
|
+
createTrace({ type: "scattergl", mode: "text+lines" }),
|
|
29
|
+
]),
|
|
30
|
+
).toBe(true);
|
|
31
|
+
});
|
|
45
32
|
|
|
46
|
-
|
|
33
|
+
it("ignores non-line and marker traces", () => {
|
|
34
|
+
expect(
|
|
35
|
+
hasPureLineTrace([
|
|
36
|
+
createTrace({ type: "scatter", mode: "markers" }),
|
|
37
|
+
createTrace({ type: "scatter", mode: "lines+markers" }),
|
|
38
|
+
createTrace({ type: "bar" }),
|
|
39
|
+
]),
|
|
40
|
+
).toBe(false);
|
|
47
41
|
});
|
|
48
42
|
});
|
|
49
43
|
|
|
50
|
-
describe("
|
|
51
|
-
it("
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
x: 3,
|
|
55
|
-
y: 7,
|
|
56
|
-
curveNumber: 0,
|
|
57
|
-
pointIndex: 1,
|
|
58
|
-
customdata: ["B"],
|
|
59
|
-
data: {
|
|
60
|
-
type: "scatter",
|
|
61
|
-
hovertemplate:
|
|
62
|
-
"label=%{customdata[0]}<br>x=%{x}<br>y=%{y}<extra></extra>",
|
|
63
|
-
},
|
|
64
|
-
}),
|
|
65
|
-
];
|
|
44
|
+
describe("lineSelectionButtons", () => {
|
|
45
|
+
it("creates dragmode buttons that update dragmode", () => {
|
|
46
|
+
const setDragmode = vi.fn();
|
|
47
|
+
const buttons = lineSelectionButtons(setDragmode);
|
|
66
48
|
|
|
67
|
-
expect(
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
label: "B",
|
|
74
|
-
},
|
|
75
|
-
]);
|
|
76
|
-
});
|
|
49
|
+
expect(buttons).toHaveLength(2);
|
|
50
|
+
expect(
|
|
51
|
+
buttons.map((button) =>
|
|
52
|
+
typeof button === "string" ? button : button.name,
|
|
53
|
+
),
|
|
54
|
+
).toEqual(["line-box-select", "line-lasso-select"]);
|
|
77
55
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
y: "Row 2",
|
|
83
|
-
z: 6,
|
|
84
|
-
curveNumber: 0,
|
|
85
|
-
pointIndex: 5,
|
|
86
|
-
data: { type: "heatmap", hovertemplate: "ignored=%{z}" },
|
|
87
|
-
}),
|
|
88
|
-
];
|
|
56
|
+
const graphDiv = document.createElement(
|
|
57
|
+
"div",
|
|
58
|
+
) as unknown as Plotly.PlotlyHTMLElement;
|
|
59
|
+
const clickEvent = new MouseEvent("click");
|
|
89
60
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
curveNumber: 0,
|
|
96
|
-
pointIndex: 5,
|
|
97
|
-
},
|
|
98
|
-
]);
|
|
61
|
+
(buttons[0] as Exclude<ModeBarButton, string>).click(graphDiv, clickEvent);
|
|
62
|
+
(buttons[1] as Exclude<ModeBarButton, string>).click(graphDiv, clickEvent);
|
|
63
|
+
|
|
64
|
+
expect(setDragmode).toHaveBeenNthCalledWith(1, "select");
|
|
65
|
+
expect(setDragmode).toHaveBeenNthCalledWith(2, "lasso");
|
|
99
66
|
});
|
|
100
67
|
});
|
|
101
68
|
|
|
102
|
-
describe("
|
|
103
|
-
it("
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
69
|
+
describe("mergeModeBarButtonsToAdd", () => {
|
|
70
|
+
it("deduplicates string buttons while preserving custom buttons", () => {
|
|
71
|
+
const customButton = {
|
|
72
|
+
name: "custom",
|
|
73
|
+
title: "Custom",
|
|
74
|
+
icon: { svg: "<svg />" },
|
|
75
|
+
click: vi.fn(),
|
|
76
|
+
} satisfies Exclude<ModeBarButton, string>;
|
|
77
|
+
|
|
78
|
+
expect(
|
|
79
|
+
mergeModeBarButtonsToAdd(
|
|
80
|
+
["zoom2d", "lasso2d"],
|
|
81
|
+
["lasso2d", customButton, "zoom2d"],
|
|
82
|
+
),
|
|
83
|
+
).toEqual(["zoom2d", "lasso2d", customButton]);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
112
86
|
|
|
113
|
-
|
|
87
|
+
describe("shouldHandleClickSelection", () => {
|
|
88
|
+
it("accepts bar clicks", () => {
|
|
89
|
+
const barPoint = createPlotDatum({
|
|
90
|
+
data: { type: "bar" },
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
expect(shouldHandleClickSelection([barPoint])).toBe(true);
|
|
114
94
|
});
|
|
115
95
|
|
|
116
|
-
it("
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const event = makeClickEvent([
|
|
121
|
-
makePoint({
|
|
122
|
-
x: "ignore",
|
|
123
|
-
y: 1,
|
|
124
|
-
pointIndex: 0,
|
|
125
|
-
data: { type: "bar" },
|
|
126
|
-
}),
|
|
127
|
-
makePoint({
|
|
128
|
-
x: 2,
|
|
129
|
-
y: 5,
|
|
130
|
-
curveNumber: 1,
|
|
131
|
-
pointIndex: 3,
|
|
132
|
-
data: { type: "scatter" },
|
|
133
|
-
}),
|
|
134
|
-
makePoint({
|
|
135
|
-
x: 4,
|
|
136
|
-
y: 12,
|
|
137
|
-
curveNumber: 2,
|
|
138
|
-
pointNumber: 5,
|
|
139
|
-
data: { type: "scattergl" },
|
|
140
|
-
}),
|
|
141
|
-
]);
|
|
96
|
+
it("accepts heatmap clicks", () => {
|
|
97
|
+
const heatmapPoint = createPlotDatum({
|
|
98
|
+
data: { type: "heatmap" },
|
|
99
|
+
});
|
|
142
100
|
|
|
143
|
-
expect(
|
|
101
|
+
expect(shouldHandleClickSelection([heatmapPoint])).toBe(true);
|
|
144
102
|
});
|
|
145
103
|
|
|
146
|
-
it("
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
pointIndex: 0,
|
|
154
|
-
data: { type: "bar" },
|
|
155
|
-
}),
|
|
156
|
-
makePoint({
|
|
157
|
-
x: 8,
|
|
158
|
-
y: 3,
|
|
159
|
-
curveNumber: 1,
|
|
160
|
-
pointNumber: 2,
|
|
161
|
-
pointNumbers: [4, 5, 6],
|
|
162
|
-
data: { type: "histogram" },
|
|
163
|
-
}),
|
|
164
|
-
]);
|
|
104
|
+
it("accepts histogram clicks", () => {
|
|
105
|
+
const histogramPoint = createPlotDatum({
|
|
106
|
+
data: { type: "histogram" },
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
expect(shouldHandleClickSelection([histogramPoint])).toBe(true);
|
|
110
|
+
});
|
|
165
111
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
indices: [2],
|
|
170
|
-
points: [
|
|
171
|
-
{
|
|
172
|
-
x: 8,
|
|
173
|
-
y: 3,
|
|
174
|
-
curveNumber: 1,
|
|
175
|
-
pointNumber: 2,
|
|
176
|
-
pointNumbers: [4, 5, 6],
|
|
177
|
-
},
|
|
178
|
-
],
|
|
112
|
+
it("accepts scatter clicks when Plotly omits mode", () => {
|
|
113
|
+
const linePoint = createPlotDatum({
|
|
114
|
+
data: { type: "scatter" },
|
|
179
115
|
});
|
|
116
|
+
|
|
117
|
+
expect(shouldHandleClickSelection([linePoint])).toBe(true);
|
|
180
118
|
});
|
|
181
119
|
|
|
182
|
-
it("
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
y: 2,
|
|
187
|
-
curveNumber: 0,
|
|
188
|
-
pointNumber: 3,
|
|
189
|
-
pointNumbers: [6, 7],
|
|
190
|
-
data: { type: "histogram" },
|
|
191
|
-
}),
|
|
192
|
-
]);
|
|
120
|
+
it("rejects non-line scatter marker clicks", () => {
|
|
121
|
+
const markerPoint = createPlotDatum({
|
|
122
|
+
data: { type: "scatter", mode: "markers" },
|
|
123
|
+
});
|
|
193
124
|
|
|
194
|
-
expect(
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
125
|
+
expect(shouldHandleClickSelection([markerPoint])).toBe(false);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
describe("extractIndices", () => {
|
|
130
|
+
it("prefers pointIndex and falls back to pointNumber and pointNumbers", () => {
|
|
131
|
+
const points = [
|
|
132
|
+
createPlotDatum({ pointIndex: 2 }),
|
|
133
|
+
createPlotDatum({ pointNumber: 5 }),
|
|
134
|
+
createPlotDatum({ pointNumbers: [Number.NaN, 8] }),
|
|
135
|
+
createPlotDatum({ pointNumbers: [Infinity] }),
|
|
136
|
+
];
|
|
137
|
+
|
|
138
|
+
expect(extractIndices(points)).toEqual([2, 5, 8]);
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
describe("extractPoints", () => {
|
|
143
|
+
it("infers missing x/y from trace data for line clicks", () => {
|
|
144
|
+
const point = createPlotDatum({
|
|
145
|
+
pointNumber: 1,
|
|
146
|
+
data: { type: "scatter" },
|
|
147
|
+
fullData: {
|
|
148
|
+
type: "scatter",
|
|
149
|
+
mode: "lines",
|
|
150
|
+
x: new Float64Array([10, 20, 30]),
|
|
151
|
+
y: [100, 200, 300],
|
|
152
|
+
},
|
|
207
153
|
});
|
|
154
|
+
|
|
155
|
+
expect(extractPoints([point])).toEqual([
|
|
156
|
+
{ pointNumber: 1, pointIndex: 1, x: 20, y: 200 },
|
|
157
|
+
]);
|
|
208
158
|
});
|
|
209
159
|
|
|
210
|
-
it("
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
160
|
+
it("parses hovertemplate values while keeping inferred point fields", () => {
|
|
161
|
+
const point = createPlotDatum({
|
|
162
|
+
pointIndex: 0,
|
|
163
|
+
customdata: ["Mustang", "USA"],
|
|
164
|
+
fullData: {
|
|
165
|
+
type: "scatter",
|
|
166
|
+
mode: "lines",
|
|
167
|
+
x: ["300"],
|
|
168
|
+
y: ["30"],
|
|
169
|
+
hovertemplate:
|
|
170
|
+
"Name=%{customdata[0]}<br>Origin=%{customdata[1]}<extra></extra>",
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
expect(extractPoints([point])).toEqual([
|
|
175
|
+
{
|
|
176
|
+
pointIndex: 0,
|
|
177
|
+
x: "300",
|
|
178
|
+
y: "30",
|
|
179
|
+
Name: "Mustang",
|
|
180
|
+
Origin: "USA",
|
|
181
|
+
},
|
|
220
182
|
]);
|
|
183
|
+
});
|
|
221
184
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
pointIndex: 10,
|
|
233
|
-
},
|
|
234
|
-
],
|
|
185
|
+
it("returns only standard fields for heatmaps", () => {
|
|
186
|
+
const point = createPlotDatum({
|
|
187
|
+
x: 1,
|
|
188
|
+
y: 2,
|
|
189
|
+
z: 3,
|
|
190
|
+
text: "ignored",
|
|
191
|
+
data: {
|
|
192
|
+
type: "heatmap",
|
|
193
|
+
hovertemplate: "Label=%{text}<extra></extra>",
|
|
194
|
+
},
|
|
235
195
|
});
|
|
196
|
+
|
|
197
|
+
expect(extractPoints([point])).toEqual([{ x: 1, y: 2, z: 3 }]);
|
|
236
198
|
});
|
|
237
199
|
});
|