@marimo-team/islands 0.19.10-dev9 → 0.19.11-dev0
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/Plot-C9vQQj4X.js +172249 -0
- package/dist/{any-language-editor-CKEbZakX.js → any-language-editor-t_VsTNa-.js} +16 -16
- package/dist/dist-4YNZxwMI.js +8 -0
- package/dist/dist-7nR3r2kG.js +5 -0
- package/dist/{dist-CBA36Nuy.js → dist-B2-r9y-0.js} +109 -109
- package/dist/dist-B2gkyT3r.js +5 -0
- package/dist/{dist-DRtGOCCq.js → dist-B4tYJP_i.js} +2 -2
- package/dist/{dist-C0e1aNzV.js → dist-B5ATpkxy.js} +2 -2
- package/dist/dist-B8G3I6vJ.js +8 -0
- package/dist/{dist-DKnxaCRl.js → dist-B94MxrQS.js} +2 -2
- package/dist/dist-BJ96Ykfp.js +8 -0
- package/dist/dist-BKLIWGw4.js +5 -0
- package/dist/{dist-l0KayR2-.js → dist-BLwfpZD-.js} +2 -2
- package/dist/{dist-CzKXtzDE.js → dist-BYmtF1W6.js} +2 -2
- package/dist/{dist-BJUs1DAG.js → dist-BbBnU4tG.js} +1 -1
- package/dist/dist-Bf3ou00A.js +6 -0
- package/dist/{dist-CITQGRtG.js → dist-BfactX3G.js} +4 -4
- package/dist/{dist-DiCjkKC2.js → dist-BoAHOW2l.js} +2 -2
- package/dist/{dist-BsBHh4jO.js → dist-Bsv_ARko.js} +4 -4
- package/dist/dist-BvkKXuPm.js +5 -0
- package/dist/{dist-DsqQCNKw.js → dist-C2-m5aEk.js} +119 -119
- package/dist/dist-C6NJ3n6r.js +5 -0
- package/dist/{dist-yI-ah_iK.js → dist-CC9VUnXd.js} +1 -1
- package/dist/{dist-tdABwZK5.js → dist-CE43BRmt.js} +1 -1
- package/dist/{dist-COp5dkis.js → dist-CJrHMxlI.js} +31 -31
- package/dist/{dist-Ct5hkOvC.js → dist-CPTE45iS.js} +1 -1
- package/dist/{dist-C-at-5cM.js → dist-CcOGT46m.js} +27 -27
- package/dist/dist-CecLPYY5.js +5 -0
- package/dist/{dist-BSMZYwqW.js → dist-Cgf353Ki.js} +1 -1
- package/dist/dist-Ch0SwRzK.js +5 -0
- package/dist/{dist-BqYNqP5W.js → dist-CkEUrAus.js} +2 -2
- package/dist/{dist-D4ObdSdT.js → dist-CmZYrgd_.js} +1 -1
- package/dist/{dist-D1q38GZb.js → dist-Crk9ejOy.js} +4 -4
- package/dist/dist-D6eWHiFh.js +6 -0
- package/dist/dist-DCQ710Bv.js +5 -0
- package/dist/{dist-r6N_0WG-.js → dist-DOil6y-3.js} +4 -4
- package/dist/{dist-CPd_adhw.js → dist-Dc1SFk5I.js} +2 -2
- package/dist/dist-Dit9tk8a.js +1242 -0
- package/dist/{dist-B7NoEgR4.js → dist-DqJdzAYM.js} +2 -2
- package/dist/dist-P_pkS5f-.js +8 -0
- package/dist/{dist-CsjsvW0K.js → dist-T4g7Sr6e.js} +3 -3
- package/dist/{dist-WETuLs_C.js → dist-glA_fIK_.js} +2 -2
- package/dist/{dist-bRBEzJF8.js → dist-iiugPhCC.js} +1 -1
- package/dist/{dist-D7jHtwN8.js → dist-r8ecBV-v.js} +135 -65
- package/dist/{dist-BlRm4v0e.js → dist-yVJ4xE5n.js} +5 -5
- package/dist/{esm-QY6C-Sev.js → esm-BAS2d2Ad.js} +1421 -1454
- package/dist/main.js +421 -383
- package/package.json +11 -11
- package/src/components/data-table/TableActions.tsx +8 -1
- package/src/components/data-table/data-table.tsx +2 -0
- package/src/components/data-table/download-actions.tsx +6 -1
- package/src/components/dependency-graph/dependency-graph-tree.tsx +10 -1
- package/src/components/dependency-graph/dependency-graph.tsx +1 -0
- package/src/components/dependency-graph/elements.ts +20 -9
- package/src/components/dependency-graph/panels.tsx +27 -11
- package/src/components/dependency-graph/types.ts +1 -0
- package/src/components/editor/chrome/wrapper/app-chrome.tsx +3 -0
- package/src/components/editor/package-alert.tsx +4 -4
- package/src/components/editor/renderers/vertical-layout/vertical-layout.tsx +3 -5
- package/src/core/codemirror/misc/__tests__/paste.test.ts +18 -0
- package/src/core/codemirror/misc/paste.ts +14 -10
- package/src/core/kernel/messages.ts +1 -0
- package/src/core/static/static-state.ts +5 -0
- package/src/core/static/types.ts +2 -0
- package/src/core/wasm/__tests__/store.test.ts +33 -0
- package/src/core/wasm/bridge.ts +2 -1
- package/src/core/wasm/store.ts +13 -1
- package/src/mount.tsx +23 -1
- package/src/plugins/impl/DataTablePlugin.tsx +4 -0
- package/src/plugins/impl/anywidget/AnyWidgetPlugin.tsx +7 -5
- package/src/plugins/impl/anywidget/__tests__/model.test.ts +53 -0
- package/src/plugins/impl/anywidget/model.ts +13 -10
- package/src/plugins/impl/chat/ChatPlugin.tsx +2 -0
- package/src/plugins/impl/chat/chat-ui.tsx +10 -1
- package/src/plugins/impl/data-frames/DataFramePlugin.tsx +4 -0
- package/src/plugins/impl/plotly/Plot.tsx +2 -0
- package/src/plugins/impl/plotly/PlotlyPlugin.tsx +36 -0
- package/src/theme/ThemeProvider.tsx +2 -0
- package/dist/Plot-CmsrWWji.js +0 -169233
- package/dist/dist-BKTAAusE.js +0 -5
- package/dist/dist-BNXv9Wjt.js +0 -6
- package/dist/dist-C-tlm9eD.js +0 -6
- package/dist/dist-CFi_P6cs.js +0 -5
- package/dist/dist-CgQuqOGS.js +0 -5
- package/dist/dist-CgkWmw0c.js +0 -5
- package/dist/dist-DGkeEIsV.js +0 -8
- package/dist/dist-DOwZz8aI.js +0 -5
- package/dist/dist-DRiJGkDN.js +0 -8
- package/dist/dist-GR6ABeNk.js +0 -8
- package/dist/dist-NcujbSeH.js +0 -8
- package/dist/dist-g1p2PEVs.js +0 -1242
- package/dist/dist-pNvwCQqJ.js +0 -5
- package/dist/dist-wudNDAiO.js +0 -5
- package/dist/dist-yK8yJfz2.js +0 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@marimo-team/islands",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.11-dev0",
|
|
4
4
|
"main": "dist/main.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -24,19 +24,19 @@
|
|
|
24
24
|
"@ai-sdk/react": "^2.0.125",
|
|
25
25
|
"@anywidget/types": "^0.2.0",
|
|
26
26
|
"@codemirror/autocomplete": "^6.20.0",
|
|
27
|
-
"@codemirror/commands": "^6.10.
|
|
27
|
+
"@codemirror/commands": "^6.10.2",
|
|
28
28
|
"@codemirror/lang-markdown": "^6.5.0",
|
|
29
29
|
"@codemirror/lang-python": "^6.2.1",
|
|
30
30
|
"@codemirror/lang-sql": "^6.10.0",
|
|
31
31
|
"@codemirror/language": "^6.12.1",
|
|
32
32
|
"@codemirror/language-data": "^6.5.2",
|
|
33
33
|
"@codemirror/legacy-modes": "^6.5.2",
|
|
34
|
-
"@codemirror/lint": "^6.9.
|
|
34
|
+
"@codemirror/lint": "^6.9.3",
|
|
35
35
|
"@codemirror/merge": "^6.11.2",
|
|
36
|
-
"@codemirror/search": "^6.
|
|
37
|
-
"@codemirror/state": "^6.5.
|
|
36
|
+
"@codemirror/search": "^6.6.0",
|
|
37
|
+
"@codemirror/state": "^6.5.4",
|
|
38
38
|
"@codemirror/theme-one-dark": "^6.1.3",
|
|
39
|
-
"@codemirror/view": "^6.39.
|
|
39
|
+
"@codemirror/view": "^6.39.12",
|
|
40
40
|
"@dagrejs/dagre": "^1.1.8",
|
|
41
41
|
"@date-fns/tz": "^1.4.1",
|
|
42
42
|
"@dnd-kit/core": "^6.3.1",
|
|
@@ -49,13 +49,13 @@
|
|
|
49
49
|
"@hookform/resolvers": "^5.2.2",
|
|
50
50
|
"@img-comparison-slider/react": "^8.0.2",
|
|
51
51
|
"@internationalized/date": "^3.10.1",
|
|
52
|
-
"@lezer/common": "^1.5.
|
|
52
|
+
"@lezer/common": "^1.5.1",
|
|
53
53
|
"@lezer/highlight": "^1.2.3",
|
|
54
|
-
"@lezer/lr": "^1.4.
|
|
55
|
-
"@lezer/markdown": "^1.6.
|
|
54
|
+
"@lezer/lr": "^1.4.8",
|
|
55
|
+
"@lezer/markdown": "^1.6.3",
|
|
56
56
|
"@lezer/python": "^1.1.18",
|
|
57
57
|
"@marimo-team/codemirror-ai": "^0.3.5",
|
|
58
|
-
"@marimo-team/codemirror-languageserver": "^1.16.
|
|
58
|
+
"@marimo-team/codemirror-languageserver": "^1.16.12",
|
|
59
59
|
"@marimo-team/codemirror-mcp": "^0.1.5",
|
|
60
60
|
"@marimo-team/codemirror-sql": "^0.2.4",
|
|
61
61
|
"@marimo-team/llm-info": "workspace:*",
|
|
@@ -144,7 +144,7 @@
|
|
|
144
144
|
"mermaid": "^11.12.2",
|
|
145
145
|
"partysocket": "1.1.10",
|
|
146
146
|
"path-to-regexp": "^8.3.0",
|
|
147
|
-
"plotly.js": "^
|
|
147
|
+
"plotly.js": "^3.3.1",
|
|
148
148
|
"pyodide": "0.27.7",
|
|
149
149
|
"react-arborist": "^3.4.3",
|
|
150
150
|
"react-aria": "3.44.0",
|
|
@@ -30,6 +30,7 @@ interface TableActionsProps<TData> {
|
|
|
30
30
|
onRowSelectionChange?: (value: RowSelectionState) => void;
|
|
31
31
|
table: Table<TData>;
|
|
32
32
|
downloadAs?: DownloadActionProps["downloadAs"];
|
|
33
|
+
downloadFileName?: string;
|
|
33
34
|
getRowIds?: GetRowIds;
|
|
34
35
|
toggleDisplayHeader?: () => void;
|
|
35
36
|
showChartBuilder?: boolean;
|
|
@@ -52,6 +53,7 @@ export const TableActions = <TData,>({
|
|
|
52
53
|
onRowSelectionChange,
|
|
53
54
|
table,
|
|
54
55
|
downloadAs,
|
|
56
|
+
downloadFileName,
|
|
55
57
|
getRowIds,
|
|
56
58
|
toggleDisplayHeader,
|
|
57
59
|
showChartBuilder,
|
|
@@ -182,7 +184,12 @@ export const TableActions = <TData,>({
|
|
|
182
184
|
/>
|
|
183
185
|
)}
|
|
184
186
|
<div className="ml-auto">
|
|
185
|
-
{downloadAs &&
|
|
187
|
+
{downloadAs && (
|
|
188
|
+
<DownloadAs
|
|
189
|
+
downloadAs={downloadAs}
|
|
190
|
+
downloadFileName={downloadFileName}
|
|
191
|
+
/>
|
|
192
|
+
)}
|
|
186
193
|
</div>
|
|
187
194
|
</div>
|
|
188
195
|
);
|
|
@@ -116,6 +116,7 @@ const DataTableInternal = <TData,>({
|
|
|
116
116
|
paginationState,
|
|
117
117
|
setPaginationState,
|
|
118
118
|
downloadAs,
|
|
119
|
+
downloadFileName,
|
|
119
120
|
manualPagination = false,
|
|
120
121
|
pagination = false,
|
|
121
122
|
onRowSelectionChange,
|
|
@@ -334,6 +335,7 @@ const DataTableInternal = <TData,>({
|
|
|
334
335
|
onRowSelectionChange={onRowSelectionChange}
|
|
335
336
|
table={table}
|
|
336
337
|
downloadAs={downloadAs}
|
|
338
|
+
downloadFileName={downloadFileName}
|
|
337
339
|
getRowIds={getRowIds}
|
|
338
340
|
toggleDisplayHeader={toggleDisplayHeader}
|
|
339
341
|
showChartBuilder={showChartBuilder}
|
|
@@ -14,6 +14,7 @@ import { logNever } from "@/utils/assertNever";
|
|
|
14
14
|
import { copyToClipboard } from "@/utils/copy";
|
|
15
15
|
import { downloadByURL } from "@/utils/download";
|
|
16
16
|
import { prettyError } from "@/utils/errors";
|
|
17
|
+
import { Filenames } from "@/utils/filenames";
|
|
17
18
|
import {
|
|
18
19
|
jsonParseWithSpecialChar,
|
|
19
20
|
jsonToMarkdown,
|
|
@@ -36,6 +37,7 @@ type DownloadFormat = "csv" | "json" | "parquet";
|
|
|
36
37
|
|
|
37
38
|
export interface DownloadActionProps {
|
|
38
39
|
downloadAs: (req: { format: DownloadFormat }) => Promise<string>;
|
|
40
|
+
downloadFileName?: string;
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
const options = [
|
|
@@ -158,7 +160,10 @@ export const DownloadAs: React.FC<DownloadActionProps> = (props) => {
|
|
|
158
160
|
onSelect={async () => {
|
|
159
161
|
const downloadUrl = await getDownloadUrl(option.format);
|
|
160
162
|
const ext = option.format;
|
|
161
|
-
|
|
163
|
+
const rawName = (props.downloadFileName ?? "").trim();
|
|
164
|
+
const baseName =
|
|
165
|
+
Filenames.withoutExtension(rawName) || "download";
|
|
166
|
+
downloadByURL(downloadUrl, `${baseName}.${ext}`);
|
|
162
167
|
}}
|
|
163
168
|
>
|
|
164
169
|
<option.icon className="mo-dropdown-icon" />
|
|
@@ -58,6 +58,7 @@ export const DependencyGraphTree: React.FC<PropsWithChildren<Props>> = ({
|
|
|
58
58
|
cellAtoms,
|
|
59
59
|
variables,
|
|
60
60
|
settings.hidePureMarkdown,
|
|
61
|
+
settings.hideReusableFunctions,
|
|
61
62
|
);
|
|
62
63
|
elements = layoutElements({
|
|
63
64
|
nodes: elements.nodes,
|
|
@@ -94,9 +95,17 @@ export const DependencyGraphTree: React.FC<PropsWithChildren<Props>> = ({
|
|
|
94
95
|
cellAtoms,
|
|
95
96
|
variables,
|
|
96
97
|
settings.hidePureMarkdown,
|
|
98
|
+
settings.hideReusableFunctions,
|
|
97
99
|
),
|
|
98
100
|
);
|
|
99
|
-
}, [
|
|
101
|
+
}, [
|
|
102
|
+
cellIds,
|
|
103
|
+
variables,
|
|
104
|
+
cellAtoms,
|
|
105
|
+
syncChanges,
|
|
106
|
+
settings.hidePureMarkdown,
|
|
107
|
+
settings.hideReusableFunctions,
|
|
108
|
+
]);
|
|
100
109
|
|
|
101
110
|
const [selection, setSelection] = useState<GraphSelection>();
|
|
102
111
|
useFitToViewOnDimensionChange();
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import type { Atom } from "jotai";
|
|
4
4
|
import { type Edge, MarkerType, type Node, type NodeProps } from "reactflow";
|
|
5
|
+
import { getNotebook } from "@/core/cells/cells";
|
|
5
6
|
import type { CellId } from "@/core/cells/ids";
|
|
6
7
|
import type { CellData } from "@/core/cells/types";
|
|
7
8
|
import { store } from "@/core/state/jotai";
|
|
@@ -29,6 +30,7 @@ interface ElementsBuilder {
|
|
|
29
30
|
cellAtoms: Atom<CellData>[],
|
|
30
31
|
variables: Variables,
|
|
31
32
|
hidePureMarkdown: boolean,
|
|
33
|
+
hideReusableFunctions: boolean,
|
|
32
34
|
) => { nodes: Node<NodeData>[]; edges: Edge[] };
|
|
33
35
|
}
|
|
34
36
|
|
|
@@ -76,6 +78,7 @@ export class VerticalElementsBuilder implements ElementsBuilder {
|
|
|
76
78
|
cellAtoms: Atom<CellData>[],
|
|
77
79
|
variables: Variables,
|
|
78
80
|
_hidePureMarkdown: boolean,
|
|
81
|
+
_hideReusableFunctions: boolean,
|
|
79
82
|
) {
|
|
80
83
|
let prevY = 0;
|
|
81
84
|
const nodes: Node<NodeData>[] = [];
|
|
@@ -143,6 +146,7 @@ export class TreeElementsBuilder implements ElementsBuilder {
|
|
|
143
146
|
cellAtoms: Atom<CellData>[],
|
|
144
147
|
variables: Variables,
|
|
145
148
|
hidePureMarkdown: boolean,
|
|
149
|
+
hideReusableFunctions: boolean,
|
|
146
150
|
) {
|
|
147
151
|
const nodes: Node<NodeData>[] = [];
|
|
148
152
|
const edges: Edge[] = [];
|
|
@@ -171,18 +175,25 @@ export class TreeElementsBuilder implements ElementsBuilder {
|
|
|
171
175
|
}
|
|
172
176
|
}
|
|
173
177
|
|
|
174
|
-
|
|
175
|
-
// Show every cell
|
|
176
|
-
if (!hidePureMarkdown) {
|
|
177
|
-
nodes.push(this.createNode(cellId, cellAtom));
|
|
178
|
-
}
|
|
178
|
+
const cellRuntime = getNotebook().cellRuntime;
|
|
179
179
|
|
|
180
|
+
for (const [cellId, cellAtom] of Arrays.zip(cellIds, cellAtoms)) {
|
|
181
|
+
const code = store.get(cellAtom).code.trim();
|
|
180
182
|
const hasEdge = nodesWithEdges.has(cellId);
|
|
181
|
-
const isMarkdown =
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
183
|
+
const isMarkdown = code.startsWith("mo.md");
|
|
184
|
+
const runtime = cellRuntime[cellId];
|
|
185
|
+
const isReusable = runtime?.serialization?.toLowerCase() === "valid";
|
|
186
|
+
|
|
187
|
+
// Apply filters
|
|
188
|
+
if (hidePureMarkdown && isMarkdown && !hasEdge) {
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
if (hideReusableFunctions && isReusable && !hasEdge) {
|
|
192
|
+
continue;
|
|
185
193
|
}
|
|
194
|
+
|
|
195
|
+
// Show every cell that wasn't filtered out
|
|
196
|
+
nodes.push(this.createNode(cellId, cellAtom));
|
|
186
197
|
}
|
|
187
198
|
|
|
188
199
|
return { nodes, edges };
|
|
@@ -43,7 +43,8 @@ export const GraphToolbar: React.FC<Props> = memo(
|
|
|
43
43
|
onSettingsChange({ ...settings, [key]: value });
|
|
44
44
|
};
|
|
45
45
|
|
|
46
|
-
const
|
|
46
|
+
const markdownCheckboxId = useId();
|
|
47
|
+
const functionsCheckboxId = useId();
|
|
47
48
|
|
|
48
49
|
const settingsButton = (
|
|
49
50
|
<Popover>
|
|
@@ -54,16 +55,31 @@ export const GraphToolbar: React.FC<Props> = memo(
|
|
|
54
55
|
</PopoverTrigger>
|
|
55
56
|
<PopoverContent className="w-auto p-2 text-muted-foreground">
|
|
56
57
|
<div className="font-semibold pb-4">Settings</div>
|
|
57
|
-
<div className="flex
|
|
58
|
-
<
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
58
|
+
<div className="flex flex-col gap-2">
|
|
59
|
+
<div className="flex items-center gap-2">
|
|
60
|
+
<Checkbox
|
|
61
|
+
data-testid="hide-pure-markdown-checkbox"
|
|
62
|
+
id={markdownCheckboxId}
|
|
63
|
+
checked={settings.hidePureMarkdown}
|
|
64
|
+
onCheckedChange={(checked) =>
|
|
65
|
+
handleSettingChange("hidePureMarkdown", Boolean(checked))
|
|
66
|
+
}
|
|
67
|
+
/>
|
|
68
|
+
<Label htmlFor={markdownCheckboxId}>Hide pure markdown</Label>
|
|
69
|
+
</div>
|
|
70
|
+
<div className="flex items-center gap-2">
|
|
71
|
+
<Checkbox
|
|
72
|
+
data-testid="hide-reusable-functions-checkbox"
|
|
73
|
+
id={functionsCheckboxId}
|
|
74
|
+
checked={settings.hideReusableFunctions}
|
|
75
|
+
onCheckedChange={(checked) =>
|
|
76
|
+
handleSettingChange("hideReusableFunctions", Boolean(checked))
|
|
77
|
+
}
|
|
78
|
+
/>
|
|
79
|
+
<Label htmlFor={functionsCheckboxId}>
|
|
80
|
+
Hide reusable functions
|
|
81
|
+
</Label>
|
|
82
|
+
</div>
|
|
67
83
|
</div>
|
|
68
84
|
</PopoverContent>
|
|
69
85
|
</Popover>
|
|
@@ -226,7 +226,9 @@ export const AppChrome: React.FC<PropsWithChildren> = ({ children }) => {
|
|
|
226
226
|
|
|
227
227
|
const helperResizeHandle = (
|
|
228
228
|
<PanelResizeHandle
|
|
229
|
+
disabled={!isSidebarOpen}
|
|
229
230
|
onDragging={handleDragging}
|
|
231
|
+
hitAreaMargins={{ coarse: 15, fine: 2 }}
|
|
230
232
|
className={cn(
|
|
231
233
|
"border-border print:hidden z-10",
|
|
232
234
|
isSidebarOpen ? "resize-handle" : "resize-handle-collapsed",
|
|
@@ -237,6 +239,7 @@ export const AppChrome: React.FC<PropsWithChildren> = ({ children }) => {
|
|
|
237
239
|
|
|
238
240
|
const panelResizeHandle = (
|
|
239
241
|
<PanelResizeHandle
|
|
242
|
+
disabled={!isDeveloperPanelOpen}
|
|
240
243
|
onDragging={handleDragging}
|
|
241
244
|
className={cn(
|
|
242
245
|
"border-border print:hidden z-20",
|
|
@@ -96,10 +96,10 @@ export const PackageAlert: React.FC = () => {
|
|
|
96
96
|
|
|
97
97
|
if (isMissingPackageAlert(packageAlert)) {
|
|
98
98
|
return (
|
|
99
|
-
<div className="flex flex-col gap-4 mb-5 fixed top-5 left-12 min-w-[400px] z-200 opacity-95 max-w-[600px]">
|
|
99
|
+
<div className="flex flex-col gap-4 mb-5 fixed top-5 left-12 min-w-[400px] z-200 opacity-95 max-w-[600px] pointer-events-none">
|
|
100
100
|
<Banner
|
|
101
101
|
kind="danger"
|
|
102
|
-
className="flex flex-col rounded py-3 px-5 animate-in slide-in-from-left overflow-auto max-h-[80vh] scrollbar-thin"
|
|
102
|
+
className="flex flex-col rounded py-3 px-5 animate-in slide-in-from-left overflow-auto max-h-[80vh] scrollbar-thin pointer-events-auto"
|
|
103
103
|
>
|
|
104
104
|
<div className="flex justify-between">
|
|
105
105
|
<span className="font-bold text-lg flex items-center mb-2">
|
|
@@ -207,10 +207,10 @@ export const PackageAlert: React.FC = () => {
|
|
|
207
207
|
}
|
|
208
208
|
|
|
209
209
|
return (
|
|
210
|
-
<div className="flex flex-col gap-4 mb-5 fixed top-5 left-12 min-w-[400px] z-200 opacity-95 max-w-[600px] ">
|
|
210
|
+
<div className="flex flex-col gap-4 mb-5 fixed top-5 left-12 min-w-[400px] z-200 opacity-95 max-w-[600px] pointer-events-none">
|
|
211
211
|
<Banner
|
|
212
212
|
kind={status === "failed" ? "danger" : "info"}
|
|
213
|
-
className="flex flex-col rounded pt-3 pb-4 px-5 overflow-auto max-h-[80vh] scrollbar-thin"
|
|
213
|
+
className="flex flex-col rounded pt-3 pb-4 px-5 overflow-auto max-h-[80vh] scrollbar-thin pointer-events-auto"
|
|
214
214
|
>
|
|
215
215
|
<div className="flex justify-between">
|
|
216
216
|
<span className="font-bold text-lg flex items-center mb-2">
|
|
@@ -172,11 +172,9 @@ const VerticalLayoutRenderer: React.FC<VerticalLayoutProps> = ({
|
|
|
172
172
|
// spacing is handled elsewhere
|
|
173
173
|
return (
|
|
174
174
|
<VerticalLayoutWrapper invisible={invisible} appConfig={appConfig}>
|
|
175
|
-
{showCode && canShowCode
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
renderCells()
|
|
179
|
-
)}
|
|
175
|
+
<div className={cn("flex flex-col", showCode && canShowCode && "gap-5")}>
|
|
176
|
+
{renderCells()}
|
|
177
|
+
</div>
|
|
180
178
|
{mode === "read" && (
|
|
181
179
|
<ActionButtons
|
|
182
180
|
canShowCode={canShowCode}
|
|
@@ -140,6 +140,24 @@ def _(
|
|
|
140
140
|
expect(extractCells(input)).toEqual(["x = a + b + c"]);
|
|
141
141
|
});
|
|
142
142
|
|
|
143
|
+
it("preserves return statements inside nested functions", () => {
|
|
144
|
+
const input = `
|
|
145
|
+
@app.cell
|
|
146
|
+
def _(mo, px):
|
|
147
|
+
def make_fig():
|
|
148
|
+
data = {'category': ['foo', 'bar'], 'value': [10, 20]}
|
|
149
|
+
fig = px.bar(data, x='category', y='value')
|
|
150
|
+
return fig
|
|
151
|
+
|
|
152
|
+
fig = make_fig()
|
|
153
|
+
mo.ui.plotly(fig)
|
|
154
|
+
return
|
|
155
|
+
`;
|
|
156
|
+
expect(extractCells(input)).toEqual([
|
|
157
|
+
"def make_fig():\n data = {'category': ['foo', 'bar'], 'value': [10, 20]}\n fig = px.bar(data, x='category', y='value')\n return fig\n\nfig = make_fig()\nmo.ui.plotly(fig)",
|
|
158
|
+
]);
|
|
159
|
+
});
|
|
160
|
+
|
|
143
161
|
it("handles cells with config", () => {
|
|
144
162
|
const input = `
|
|
145
163
|
@app.cell(hide_code=True, column=2)
|
|
@@ -42,6 +42,7 @@ export function extractCells(text: string): string[] {
|
|
|
42
42
|
let inMultilineArgs = false;
|
|
43
43
|
let inMultilineReturn = false;
|
|
44
44
|
let parenCount = 0;
|
|
45
|
+
let cellBaseIndent: number | null = null;
|
|
45
46
|
|
|
46
47
|
// Pre-compile regex patterns
|
|
47
48
|
const leadingParenRegex = /\(/g;
|
|
@@ -55,19 +56,16 @@ export function extractCells(text: string): string[] {
|
|
|
55
56
|
);
|
|
56
57
|
}
|
|
57
58
|
|
|
59
|
+
function getIndent(line: string): number {
|
|
60
|
+
const match = line.match(/^\s*/);
|
|
61
|
+
return match ? match[0].length : 0;
|
|
62
|
+
}
|
|
63
|
+
|
|
58
64
|
function finalizeCellIfNeeded() {
|
|
59
65
|
if (currentCell.length === 0) {
|
|
60
66
|
return;
|
|
61
67
|
}
|
|
62
68
|
|
|
63
|
-
// Remove trailing returns
|
|
64
|
-
while (
|
|
65
|
-
currentCell.length > 0 &&
|
|
66
|
-
currentCell[currentCell.length - 1].trim().startsWith("return")
|
|
67
|
-
) {
|
|
68
|
-
currentCell.pop();
|
|
69
|
-
}
|
|
70
|
-
|
|
71
69
|
// Only add non-empty cells
|
|
72
70
|
if (currentCell.some((l) => l.trim() !== "")) {
|
|
73
71
|
cells.push(dedent(currentCell.join("\n")));
|
|
@@ -88,6 +86,7 @@ export function extractCells(text: string): string[] {
|
|
|
88
86
|
finalizeCellIfNeeded();
|
|
89
87
|
inCell = true;
|
|
90
88
|
skipLines = 1; // Skip the def line
|
|
89
|
+
cellBaseIndent = null;
|
|
91
90
|
continue;
|
|
92
91
|
}
|
|
93
92
|
|
|
@@ -125,8 +124,13 @@ export function extractCells(text: string): string[] {
|
|
|
125
124
|
continue;
|
|
126
125
|
}
|
|
127
126
|
|
|
128
|
-
//
|
|
129
|
-
if (trimmed
|
|
127
|
+
// Detect base indentation of cell body from first content line
|
|
128
|
+
if (cellBaseIndent === null && trimmed) {
|
|
129
|
+
cellBaseIndent = getIndent(line);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Handle return statements — only strip cell-level returns
|
|
133
|
+
if (trimmed.startsWith("return") && getIndent(line) === cellBaseIndent) {
|
|
130
134
|
if (trimmed.includes("(") && !trimmed.endsWith(")")) {
|
|
131
135
|
inMultilineReturn = true;
|
|
132
136
|
parenCount = countParens(trimmed);
|
|
@@ -13,6 +13,7 @@ export const DATA_TYPES = [
|
|
|
13
13
|
"time",
|
|
14
14
|
"unknown",
|
|
15
15
|
] as const;
|
|
16
|
+
export type ModelLifecycle = NotificationMessageData<"model-lifecycle">;
|
|
16
17
|
export type Banner = NotificationMessageData<"banner">;
|
|
17
18
|
export type AiInlineCompletionRequest = schemas["AiInlineCompletionRequest"];
|
|
18
19
|
export type DataTableColumn = schemas["DataTableColumn"];
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
import { invariant } from "@/utils/invariant";
|
|
3
|
+
import type { ModelLifecycle } from "../kernel/messages";
|
|
3
4
|
import type { MarimoStaticState, StaticVirtualFiles } from "./types";
|
|
4
5
|
|
|
5
6
|
declare global {
|
|
@@ -17,3 +18,7 @@ export function getStaticVirtualFiles(): StaticVirtualFiles {
|
|
|
17
18
|
|
|
18
19
|
return window.__MARIMO_STATIC__.files;
|
|
19
20
|
}
|
|
21
|
+
|
|
22
|
+
export function getStaticModelNotifications(): ModelLifecycle[] | undefined {
|
|
23
|
+
return window?.__MARIMO_STATIC__?.modelNotifications;
|
|
24
|
+
}
|
package/src/core/static/types.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
import type { DataURLString } from "@/utils/json/base64";
|
|
3
|
+
import type { ModelLifecycle } from "../kernel/messages";
|
|
3
4
|
|
|
4
5
|
export type StaticVirtualFiles = Record<string, DataURLString>;
|
|
5
6
|
|
|
6
7
|
export interface MarimoStaticState {
|
|
7
8
|
files: StaticVirtualFiles;
|
|
9
|
+
modelNotifications?: ModelLifecycle[];
|
|
8
10
|
}
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
import { describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { codeAtom } from "../../saving/file-state";
|
|
4
|
+
import { store } from "../../state/jotai";
|
|
3
5
|
import {
|
|
4
6
|
CompositeFileStore,
|
|
5
7
|
domElementFileStore,
|
|
6
8
|
localStorageFileStore,
|
|
9
|
+
mountConfigFileStore,
|
|
10
|
+
notebookFileStore,
|
|
7
11
|
} from "../store";
|
|
8
12
|
|
|
9
13
|
describe("localStorageFileStore", () => {
|
|
@@ -53,3 +57,32 @@ describe("CompositeFileStore", () => {
|
|
|
53
57
|
expect(saved).toHaveBeenCalledTimes(3);
|
|
54
58
|
});
|
|
55
59
|
});
|
|
60
|
+
|
|
61
|
+
describe("mountConfigFileStore", () => {
|
|
62
|
+
it("returns null when no code is set", () => {
|
|
63
|
+
store.set(codeAtom, undefined);
|
|
64
|
+
expect(mountConfigFileStore.readFile()).toBeNull();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("returns code when set", () => {
|
|
68
|
+
store.set(codeAtom, "print('hello')");
|
|
69
|
+
expect(mountConfigFileStore.readFile()).toBe("print('hello')");
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("does not save files", () => {
|
|
73
|
+
store.set(codeAtom, "original");
|
|
74
|
+
mountConfigFileStore.saveFile("new content");
|
|
75
|
+
expect(mountConfigFileStore.readFile()).toBe("original");
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe("notebookFileStore priority", () => {
|
|
80
|
+
it("prefers mount config over marimo-code and URL", () => {
|
|
81
|
+
store.set(codeAtom, "mount config code");
|
|
82
|
+
const element = document.createElement("marimo-code");
|
|
83
|
+
element.textContent = "marimo-code element";
|
|
84
|
+
document.body.replaceChildren(element);
|
|
85
|
+
|
|
86
|
+
expect(notebookFileStore.readFile()).toBe("mount config code");
|
|
87
|
+
});
|
|
88
|
+
});
|
package/src/core/wasm/bridge.ts
CHANGED
|
@@ -29,6 +29,7 @@ import type {
|
|
|
29
29
|
SaveUserConfigurationRequest,
|
|
30
30
|
Snippets,
|
|
31
31
|
} from "../network/types";
|
|
32
|
+
import { filenameAtom } from "../saving/file-state";
|
|
32
33
|
import { store } from "../state/jotai";
|
|
33
34
|
import { BasicTransport } from "../websocket/transports/basic";
|
|
34
35
|
import type { IConnectionTransport } from "../websocket/transports/transport";
|
|
@@ -146,7 +147,7 @@ export class PyodideBridge implements RunRequests, EditRequests {
|
|
|
146
147
|
|
|
147
148
|
const code = await notebookFileStore.readFile();
|
|
148
149
|
const fallbackCode = await fallbackFileStore.readFile();
|
|
149
|
-
const filename = PyodideRouter.getFilename();
|
|
150
|
+
const filename = store.get(filenameAtom) ?? PyodideRouter.getFilename();
|
|
150
151
|
const userConfig = store.get(userConfigAtom);
|
|
151
152
|
|
|
152
153
|
const queryParameters: Record<string, string | string[]> = {};
|
package/src/core/wasm/store.ts
CHANGED
|
@@ -4,6 +4,8 @@ import {
|
|
|
4
4
|
compressToEncodedURIComponent,
|
|
5
5
|
decompressFromEncodedURIComponent,
|
|
6
6
|
} from "lz-string";
|
|
7
|
+
import { codeAtom } from "@/core/saving/file-state";
|
|
8
|
+
import { store } from "@/core/state/jotai";
|
|
7
9
|
import { TypedLocalStorage } from "@/utils/storage/typed";
|
|
8
10
|
import { PyodideRouter } from "./router";
|
|
9
11
|
|
|
@@ -67,6 +69,15 @@ const remoteDefaultFileStore: FileStore = {
|
|
|
67
69
|
},
|
|
68
70
|
};
|
|
69
71
|
|
|
72
|
+
export const mountConfigFileStore: FileStore = {
|
|
73
|
+
saveFile(_contents: string) {
|
|
74
|
+
// Read-only: mount config code is set via codeAtom
|
|
75
|
+
},
|
|
76
|
+
readFile() {
|
|
77
|
+
return store.get(codeAtom) ?? null;
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
|
|
70
81
|
const emptyFileStore: FileStore = {
|
|
71
82
|
saveFile(contents: string) {
|
|
72
83
|
// Do nothing
|
|
@@ -112,7 +123,8 @@ export class CompositeFileStore implements FileStore {
|
|
|
112
123
|
}
|
|
113
124
|
|
|
114
125
|
export const notebookFileStore = new CompositeFileStore([
|
|
115
|
-
// Prefer <marimo-code>, then URL
|
|
126
|
+
// Prefer mount config, then <marimo-code>, then URL
|
|
127
|
+
mountConfigFileStore,
|
|
116
128
|
domElementFileStore,
|
|
117
129
|
urlFileStore,
|
|
118
130
|
]);
|
package/src/mount.tsx
CHANGED
|
@@ -39,11 +39,18 @@ import {
|
|
|
39
39
|
import { codeAtom, filenameAtom } from "./core/saving/file-state";
|
|
40
40
|
import { store } from "./core/state/jotai";
|
|
41
41
|
import { patchFetch, patchVegaLoader } from "./core/static/files";
|
|
42
|
-
import {
|
|
42
|
+
import {
|
|
43
|
+
getStaticModelNotifications,
|
|
44
|
+
isStaticNotebook,
|
|
45
|
+
} from "./core/static/static-state";
|
|
43
46
|
import { maybeRegisterVSCodeBindings } from "./core/vscode/vscode-bindings";
|
|
44
47
|
import type { FileStore } from "./core/wasm/store";
|
|
45
48
|
import { notebookFileStore } from "./core/wasm/store";
|
|
46
49
|
import { WebSocketState } from "./core/websocket/types";
|
|
50
|
+
import {
|
|
51
|
+
handleWidgetMessage,
|
|
52
|
+
MODEL_MANAGER,
|
|
53
|
+
} from "./plugins/impl/anywidget/model";
|
|
47
54
|
import { vegaLoader } from "./plugins/impl/vega/loader";
|
|
48
55
|
import { initializePlugins } from "./plugins/plugins";
|
|
49
56
|
import { ThemeProvider } from "./theme/ThemeProvider";
|
|
@@ -77,6 +84,7 @@ export function mount(options: unknown, el: Element): Error | undefined {
|
|
|
77
84
|
// If we're in static mode, we need to patch fetch to use the virtual file
|
|
78
85
|
patchFetch();
|
|
79
86
|
patchVegaLoader(vegaLoader);
|
|
87
|
+
hydrateStaticModels();
|
|
80
88
|
}
|
|
81
89
|
|
|
82
90
|
// Init store
|
|
@@ -330,6 +338,20 @@ function initStore(options: unknown) {
|
|
|
330
338
|
}
|
|
331
339
|
}
|
|
332
340
|
|
|
341
|
+
/**
|
|
342
|
+
* Hydrate anywidget models from embedded static state so widgets
|
|
343
|
+
* render immediately without a kernel connection.
|
|
344
|
+
*/
|
|
345
|
+
function hydrateStaticModels(): void {
|
|
346
|
+
const notifications = getStaticModelNotifications();
|
|
347
|
+
if (!notifications) {
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
for (const notification of notifications) {
|
|
351
|
+
handleWidgetMessage(MODEL_MANAGER, notification);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
333
355
|
export const visibleForTesting = {
|
|
334
356
|
reset: () => {
|
|
335
357
|
hasMounted = false;
|
|
@@ -195,6 +195,7 @@ interface Data<T> {
|
|
|
195
195
|
hasStableRowId: boolean;
|
|
196
196
|
lazy: boolean;
|
|
197
197
|
cellHoverTexts?: Record<string, Record<string, string | null>> | null;
|
|
198
|
+
downloadFileName?: string;
|
|
198
199
|
}
|
|
199
200
|
|
|
200
201
|
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
|
|
@@ -278,6 +279,7 @@ export const DataTablePlugin = createPlugin<S>("marimo-table")
|
|
|
278
279
|
// If lazy, this will preload the first page of data
|
|
279
280
|
// without user confirmation.
|
|
280
281
|
preload: z.boolean().default(false),
|
|
282
|
+
downloadFileName: z.string().optional(),
|
|
281
283
|
}),
|
|
282
284
|
)
|
|
283
285
|
.withFunctions<DataTableFunctions>({
|
|
@@ -752,6 +754,7 @@ const DataTableComponent = ({
|
|
|
752
754
|
cellStyles,
|
|
753
755
|
hoverTemplate,
|
|
754
756
|
cellHoverTexts,
|
|
757
|
+
downloadFileName,
|
|
755
758
|
toggleDisplayHeader,
|
|
756
759
|
calculate_top_k_rows,
|
|
757
760
|
preview_column,
|
|
@@ -981,6 +984,7 @@ const DataTableComponent = ({
|
|
|
981
984
|
hoverTemplate={hoverTemplate}
|
|
982
985
|
cellHoverTexts={cellHoverTexts}
|
|
983
986
|
downloadAs={showDownload ? downloadAs : undefined}
|
|
987
|
+
downloadFileName={downloadFileName}
|
|
984
988
|
enableSearch={enableSearch}
|
|
985
989
|
searchQuery={searchQuery}
|
|
986
990
|
onSearchQueryChange={setSearchQuery}
|