@marimo-team/islands 0.24.1-dev113 → 0.24.1-dev115
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-CF7VGRML.js → ConnectedDataExplorerComponent-3vyrwCHu.js} +12 -12
- package/dist/{ErrorBoundary-Ca9Ulp6E.js → ErrorBoundary-QF0xsxMw.js} +3 -3
- package/dist/{any-language-editor-D6lJfTIq.js → any-language-editor-B2e3EkTe.js} +4 -4
- package/dist/{button-BeWeF5Z2.js → button-CKBkqR9J.js} +1 -1
- package/dist/{capabilities-C7Q0J4_N.js → capabilities-CMBBJTcc.js} +1 -1
- package/dist/{chat-ui-DwtGBnE7.js → chat-ui-BgGHMO2G.js} +16 -16
- package/dist/{check-DGKNz9MB.js → check-C_-c2CXl.js} +1 -1
- package/dist/{common-CHiGez3b.js → common-Cdx64lyR.js} +23 -23
- package/dist/{copy-T56I_gVc.js → copy-D4GHy7GA.js} +2 -2
- package/dist/{dist-CSjGX93W.js → dist-mymH-A_x.js} +2 -2
- package/dist/{dropdown-menu-D-pePvPo.js → dropdown-menu-Div6gwlw.js} +6 -6
- package/dist/{error-banner-C0gx40vn.js → error-banner-DmgOcFS3.js} +2 -2
- package/dist/{esm-aqYR5Dl6.js → esm-CmFEQJjP.js} +1 -1
- package/dist/{extends-G2pVMdwU.js → extends-CGTKDWHv.js} +4 -4
- package/dist/{formats-CX_fkiT0.js → formats-DuRRWVwb.js} +3 -3
- package/dist/{glide-data-editor-BXnH2j9C.js → glide-data-editor-ekJ5GwaZ.js} +8 -8
- package/dist/{glide-utils-AvKWWLSq.js → glide-utils-Dwc8Eh8G.js} +3 -3
- package/dist/{html-to-image-Cf3vfRXj.js → html-to-image-u8Ik6EnK.js} +20 -20
- package/dist/{label-BUGMEGks.js → label-DdGS_CZZ.js} +2 -2
- package/dist/{loader-BIZus5lj.js → loader-VZ__bv4g.js} +1 -1
- package/dist/main.js +38 -38
- package/dist/{mermaid-CzrpWuKV.js → mermaid-Bt45Zzl4.js} +4 -4
- package/dist/{process-output-BZs2qaau.js → process-output-DQiqt-_U.js} +3 -3
- package/dist/{reveal-component-BSz_VbT2.js → reveal-component-tQ-rgxcQ.js} +9 -9
- package/dist/{schemas-CLB4UWgG.js → schemas-BjeOpZLk.js} +1 -1
- package/dist/{select-foAi7oM_.js → select-DotAQ4jp.js} +12 -12
- package/dist/{spec-JFqQpvZq.js → spec-BesaYkGC.js} +4 -4
- package/dist/style.css +1 -1
- package/dist/{swiper-component-Bqzl6az1.js → swiper-component-C90YjhaV.js} +2 -2
- package/dist/{toDate-DxgaJjev.js → toDate-CrAIOASL.js} +3 -3
- package/dist/{tooltip-B1X8ECYn.js → tooltip-COXZxyl7.js} +3 -3
- package/dist/{useAsyncData-BkvtR_Y2.js → useAsyncData-x0_tWDWI.js} +1 -1
- package/dist/{useDateFormatter-DGIhtjdF.js → useDateFormatter-B3V__WqX.js} +2 -2
- package/dist/{useDeepCompareMemoize-DcZEHOmt.js → useDeepCompareMemoize-KaRHJEEU.js} +1 -1
- package/dist/{useIframeCapabilities-Bj4ORYGz.js → useIframeCapabilities-De3C-9b4.js} +1 -1
- package/dist/{useLifecycle-BHpzFL9q.js → useLifecycle-x0OriHo-.js} +10 -10
- package/dist/{useTheme-BlOgV7zf.js → useTheme-Bm9_FyqO.js} +2 -2
- package/dist/{vega-component-B4rwjseP.js → vega-component-nD2oP6WN.js} +9 -9
- package/package.json +1 -1
- package/src/components/dependency-graph/__tests__/elements.test.ts +151 -0
- package/src/components/dependency-graph/custom-edge.tsx +68 -0
- package/src/components/dependency-graph/custom-node.tsx +54 -26
- package/src/components/dependency-graph/dependency-graph-tree.tsx +110 -21
- package/src/components/dependency-graph/elements.ts +225 -31
- package/src/components/dependency-graph/panels.tsx +23 -2
- package/src/components/dependency-graph/utils/__tests__/layout.test.ts +78 -0
- package/src/components/dependency-graph/utils/layout.ts +108 -18
- package/src/components/ui/__tests__/badge.test.ts +63 -0
- package/src/components/ui/__tests__/button.test.ts +19 -0
- package/src/components/ui/__tests__/menu-items.test.ts +31 -0
- package/src/components/ui/badge.tsx +9 -7
- package/src/components/ui/button.tsx +1 -0
- package/src/components/ui/combobox.tsx +1 -1
- package/src/components/ui/date-input.tsx +1 -1
- package/src/components/ui/dialog.tsx +3 -1
- package/src/components/ui/menu-items.tsx +9 -9
- package/src/components/ui/native-select.tsx +2 -2
- package/src/components/ui/navigation.tsx +1 -1
- package/src/components/ui/range-slider.tsx +5 -3
- package/src/components/ui/select.tsx +1 -1
- package/src/components/ui/sheet.tsx +1 -1
- package/src/components/ui/toast.tsx +8 -5
- package/src/components/ui/toaster.tsx +2 -2
- package/src/css/globals.css +28 -0
- package/src/plugins/impl/data-frames/forms/__tests__/__snapshots__/form.test.tsx.snap +25 -25
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { a as e } from "./rolldown-runtime-Cb76FRpK.js";
|
|
2
|
-
import { c as t, h as n, t as r } from "./button-
|
|
2
|
+
import { c as t, h as n, t as r } from "./button-CKBkqR9J.js";
|
|
3
3
|
import { t as i } from "./react-DMWcHin-.js";
|
|
4
4
|
import { t as a } from "./compiler-runtime-CAH9RLw2.js";
|
|
5
5
|
import { t as o } from "./jsx-runtime-CpoKNRgu.js";
|
|
6
|
-
import { t as s } from "./useIframeCapabilities-
|
|
6
|
+
import { t as s } from "./useIframeCapabilities-De3C-9b4.js";
|
|
7
7
|
/* empty css */
|
|
8
8
|
//#region ../node_modules/.pnpm/swiper@12.1.2/node_modules/swiper/shared/ssr-window.esm.mjs
|
|
9
9
|
var c = /* @__PURE__ */ e(i(), 1), l = a();
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { t as e } from "./rolldown-runtime-Cb76FRpK.js";
|
|
2
|
-
import { g as t } from "./button-
|
|
2
|
+
import { g as t } from "./button-CKBkqR9J.js";
|
|
3
3
|
import { t as n } from "./compiler-runtime-CAH9RLw2.js";
|
|
4
|
-
import { u as r } from "./dist-
|
|
5
|
-
import { E as i, _ as a, a as o, b as s } from "./useTheme-
|
|
4
|
+
import { u as r } from "./dist-mymH-A_x.js";
|
|
5
|
+
import { E as i, _ as a, a as o, b as s } from "./useTheme-Bm9_FyqO.js";
|
|
6
6
|
import { t as c } from "./invariant-DLmrl-hA.js";
|
|
7
7
|
var l = r("circle-question-mark", [
|
|
8
8
|
["circle", {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { a as e } from "./rolldown-runtime-Cb76FRpK.js";
|
|
2
|
-
import { d as t, h as n, p as r } from "./button-
|
|
2
|
+
import { d as t, h as n, p as r } from "./button-CKBkqR9J.js";
|
|
3
3
|
import { t as i } from "./react-DMWcHin-.js";
|
|
4
4
|
import { t as a } from "./compiler-runtime-CAH9RLw2.js";
|
|
5
|
-
import { a as o, i as s, n as c, r as l, s as u, t as d } from "./dist-
|
|
5
|
+
import { a as o, i as s, n as c, r as l, s as u, t as d } from "./dist-mymH-A_x.js";
|
|
6
6
|
import { t as f } from "./jsx-runtime-CpoKNRgu.js";
|
|
7
|
-
import { Bc as p, Dc as m, Ec as h, Hc as ee, Kc as g, Lc as _, Pc as v, Vc as y, Wc as b, kc as x, qc as S } from "./schemas-
|
|
7
|
+
import { Bc as p, Dc as m, Ec as h, Hc as ee, Kc as g, Lc as _, Pc as v, Vc as y, Wc as b, kc as x, qc as S } from "./schemas-BjeOpZLk.js";
|
|
8
8
|
//#region ../node_modules/.pnpm/@radix-ui+react-tooltip@1.2.8_@types+react-dom@19.2.3_@types+react@19.2.10__@types+reac_6c936392a1aa34d496e116dc55299197/node_modules/@radix-ui/react-tooltip/dist/index.mjs
|
|
9
9
|
var C = /* @__PURE__ */ e(i(), 1), w = f(), [T, E] = g("Tooltip", [o]), D = o(), O = "TooltipProvider", te = 700, k = "tooltip.open", [ne, A] = T(O), j = (e) => {
|
|
10
10
|
let { __scopeTooltip: t, delayDuration: n = te, skipDelayDuration: r = 300, disableHoverableContent: i = !1, children: a } = e, o = C.useRef(!0), s = C.useRef(!1), c = C.useRef(0);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { a as e } from "./rolldown-runtime-Cb76FRpK.js";
|
|
2
2
|
import { t } from "./react-DMWcHin-.js";
|
|
3
3
|
import { t as n } from "./compiler-runtime-CAH9RLw2.js";
|
|
4
|
-
import { j as r } from "./useTheme-
|
|
4
|
+
import { j as r } from "./useTheme-Bm9_FyqO.js";
|
|
5
5
|
import { t as i } from "./invariant-DLmrl-hA.js";
|
|
6
6
|
//#region src/hooks/useAsyncData.ts
|
|
7
7
|
var a = n(), o = /* @__PURE__ */ e(t(), 1), s = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { a as e } from "./rolldown-runtime-Cb76FRpK.js";
|
|
2
2
|
import { t } from "./react-DMWcHin-.js";
|
|
3
|
-
import { u as n } from "./dist-
|
|
4
|
-
import { E as r } from "./select-
|
|
3
|
+
import { u as n } from "./dist-mymH-A_x.js";
|
|
4
|
+
import { E as r } from "./select-DotAQ4jp.js";
|
|
5
5
|
var i = n("chart-pie", [["path", {
|
|
6
6
|
d: "M21 12c.552 0 1.005-.449.95-.998a10 10 0 0 0-8.953-8.951c-.55-.055-.998.398-.998.95v8a1 1 0 0 0 1 1z",
|
|
7
7
|
key: "pzmjnu"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { a as e } from "./rolldown-runtime-Cb76FRpK.js";
|
|
2
2
|
import { t } from "./react-DMWcHin-.js";
|
|
3
|
-
import { A as n } from "./useTheme-
|
|
3
|
+
import { A as n } from "./useTheme-Bm9_FyqO.js";
|
|
4
4
|
//#region src/hooks/useDeepCompareMemoize.ts
|
|
5
5
|
var r = /* @__PURE__ */ e(t(), 1);
|
|
6
6
|
function i(e) {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { a as e } from "./rolldown-runtime-Cb76FRpK.js";
|
|
2
|
-
import { g as t, h as n, r } from "./button-
|
|
2
|
+
import { g as t, h as n, r } from "./button-CKBkqR9J.js";
|
|
3
3
|
import { t as i } from "./react-DMWcHin-.js";
|
|
4
4
|
import { t as a } from "./compiler-runtime-CAH9RLw2.js";
|
|
5
|
-
import { u as o } from "./dist-
|
|
5
|
+
import { u as o } from "./dist-mymH-A_x.js";
|
|
6
6
|
import { t as s } from "./jsx-runtime-CpoKNRgu.js";
|
|
7
|
-
import { E as c, w as l } from "./useTheme-
|
|
7
|
+
import { E as c, w as l } from "./useTheme-Bm9_FyqO.js";
|
|
8
8
|
var u = o("calendar", [
|
|
9
9
|
["path", {
|
|
10
10
|
d: "M8 2v4",
|
|
@@ -136,14 +136,14 @@ function g(e, n, r) {
|
|
|
136
136
|
}
|
|
137
137
|
//#endregion
|
|
138
138
|
//#region src/components/ui/badge.tsx
|
|
139
|
-
var _ = /* @__PURE__ */ e(i(), 1), v = s(), y = r("inline-flex items-center border rounded-full px-2 py-0.5 text-xs font-semibold transition-colors focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2", {
|
|
139
|
+
var _ = /* @__PURE__ */ e(i(), 1), v = s(), y = r("inline-flex items-center border rounded-full px-2 py-0.5 text-xs font-semibold transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 forced-color-adjust-none", {
|
|
140
140
|
variants: { variant: {
|
|
141
|
-
default: "bg-primary hover:bg-primary/60 border-transparent text-primary-foreground",
|
|
142
|
-
defaultOutline: "bg-(--blue-2) border-(--blue-8) text-(--blue-11)",
|
|
143
|
-
secondary: "bg-secondary hover:bg-secondary/80 border-transparent text-secondary-foreground",
|
|
144
|
-
destructive: "bg-(--red-2) border-(--red-6) text-(--red-9) hover:bg-(--red-3)",
|
|
145
|
-
success: "bg-(--grass-2) border-(--grass-5) text-(--grass-9) hover:bg-(--grass-3)",
|
|
146
|
-
outline: "text-foreground"
|
|
141
|
+
default: "bg-primary hover:bg-primary/60 border-transparent text-primary-foreground forced-colors:bg-[Highlight] forced-colors:border-[Highlight] forced-colors:text-[HighlightText] forced-colors:hover:bg-[Highlight] forced-colors:hover:text-[HighlightText]",
|
|
142
|
+
defaultOutline: "bg-(--blue-2) border-(--blue-8) text-(--blue-11) forced-colors:bg-[Canvas] forced-colors:border-[Highlight] forced-colors:text-[CanvasText]",
|
|
143
|
+
secondary: "bg-secondary hover:bg-secondary/80 border-transparent text-secondary-foreground forced-colors:bg-[ButtonFace] forced-colors:border-[ButtonText] forced-colors:text-[ButtonText] forced-colors:hover:bg-[Highlight] forced-colors:hover:text-[HighlightText]",
|
|
144
|
+
destructive: "bg-(--red-2) border-(--red-6) text-(--red-9) hover:bg-(--red-3) forced-colors:bg-[Canvas] forced-colors:border-[ButtonText] forced-colors:text-[CanvasText] forced-colors:hover:bg-[Highlight] forced-colors:hover:text-[HighlightText]",
|
|
145
|
+
success: "bg-(--grass-2) border-(--grass-5) text-(--grass-9) hover:bg-(--grass-3) forced-colors:bg-[Canvas] forced-colors:border-[ButtonText] forced-colors:text-[CanvasText] forced-colors:hover:bg-[Highlight] forced-colors:hover:text-[HighlightText]",
|
|
146
|
+
outline: "text-foreground forced-colors:bg-[Canvas] forced-colors:border-[ButtonText] forced-colors:text-[CanvasText]"
|
|
147
147
|
} },
|
|
148
148
|
defaultVariants: { variant: "default" }
|
|
149
149
|
}), b = (e) => {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { a as e } from "./rolldown-runtime-Cb76FRpK.js";
|
|
2
|
-
import { a as t, g as n, s as r } from "./button-
|
|
2
|
+
import { a as t, g as n, s as r } from "./button-CKBkqR9J.js";
|
|
3
3
|
import { t as i } from "./react-DMWcHin-.js";
|
|
4
4
|
import { t as a } from "./compiler-runtime-CAH9RLw2.js";
|
|
5
|
-
import { Cn as o, In as s, Jn as c, Sn as l, Tt as u, jn as d, kt as f, un as p, vt as m } from "./schemas-
|
|
5
|
+
import { Cn as o, In as s, Jn as c, Sn as l, Tt as u, jn as d, kt as f, un as p, vt as m } from "./schemas-BjeOpZLk.js";
|
|
6
6
|
import { n as h, t as g } from "./_baseMerge-Dv8bBloN.js";
|
|
7
7
|
//#region ../node_modules/.pnpm/lodash-es@4.18.1/node_modules/lodash-es/mergeWith.js
|
|
8
8
|
var _ = h(function(e, t, n, r) {
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import { a as e } from "./rolldown-runtime-Cb76FRpK.js";
|
|
2
|
-
import { g as t, h as n, m as r, o as i } from "./button-
|
|
2
|
+
import { g as t, h as n, m as r, o as i } from "./button-CKBkqR9J.js";
|
|
3
3
|
import { t as a } from "./react-DMWcHin-.js";
|
|
4
4
|
import { t as o } from "./compiler-runtime-CAH9RLw2.js";
|
|
5
|
-
import { c as s, v as c } from "./toDate-
|
|
5
|
+
import { c as s, v as c } from "./toDate-CrAIOASL.js";
|
|
6
6
|
import { t as l } from "./jsx-runtime-CpoKNRgu.js";
|
|
7
|
-
import { n as u } from "./error-banner-
|
|
8
|
-
import { t as d } from "./tooltip-
|
|
7
|
+
import { n as u } from "./error-banner-DmgOcFS3.js";
|
|
8
|
+
import { t as d } from "./tooltip-COXZxyl7.js";
|
|
9
9
|
import { t as f } from "./debounce-C3jyATpc.js";
|
|
10
|
-
import { j as p, n as ee } from "./useTheme-
|
|
10
|
+
import { j as p, n as ee } from "./useTheme-Bm9_FyqO.js";
|
|
11
11
|
import { c as m } from "./arrays-CN1djC4k.js";
|
|
12
12
|
import { t as te } from "./objects-CzUgoMIF.js";
|
|
13
|
-
import { t as h } from "./useAsyncData-
|
|
14
|
-
import { a as ne, i as re, n as ie, t as g } from "./formats-
|
|
13
|
+
import { t as h } from "./useAsyncData-x0_tWDWI.js";
|
|
14
|
+
import { a as ne, i as re, n as ie, t as g } from "./formats-DuRRWVwb.js";
|
|
15
15
|
import { A as ae, n as oe } from "./vega-loader.browser-DEggZzni.js";
|
|
16
16
|
import { t as se } from "./react-vega-DSD5aT7_.js";
|
|
17
|
-
import { a as ce, n as _ } from "./loader-
|
|
18
|
-
import { t as le } from "./useDeepCompareMemoize-
|
|
17
|
+
import { a as ce, n as _ } from "./loader-VZ__bv4g.js";
|
|
18
|
+
import { t as le } from "./useDeepCompareMemoize-KaRHJEEU.js";
|
|
19
19
|
import { t as ue } from "./semaphore-89jsoHtS.js";
|
|
20
20
|
import { t as de } from "./use-vega-container-remeasure-DUW4tRxq.js";
|
|
21
21
|
//#region src/plugins/impl/vega/fix-relative-url.ts
|
package/package.json
CHANGED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
4
|
+
import type { CellId } from "@/core/cells/ids";
|
|
5
|
+
import type { Variable, VariableName, Variables } from "@/core/variables/types";
|
|
6
|
+
import { createCellRuntimeState } from "@/core/cells/types";
|
|
7
|
+
import {
|
|
8
|
+
collapsedNodeWidth,
|
|
9
|
+
computeDefsByCell,
|
|
10
|
+
isStateFlowVariable,
|
|
11
|
+
reusableCellIds,
|
|
12
|
+
wrapDefs,
|
|
13
|
+
} from "../elements";
|
|
14
|
+
|
|
15
|
+
const names = (list: string[]) => list as VariableName[];
|
|
16
|
+
|
|
17
|
+
function variable(
|
|
18
|
+
partial: Omit<Partial<Variable>, "name"> & { name: string },
|
|
19
|
+
): Variable {
|
|
20
|
+
return {
|
|
21
|
+
declaredBy: [],
|
|
22
|
+
usedBy: [],
|
|
23
|
+
...partial,
|
|
24
|
+
name: partial.name as VariableName,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
describe("collapsedNodeWidth", () => {
|
|
29
|
+
it("clamps short labels to the minimum width", () => {
|
|
30
|
+
expect(collapsedNodeWidth("")).toBe(90);
|
|
31
|
+
expect(collapsedNodeWidth("x")).toBe(90);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("clamps long labels to the maximum width", () => {
|
|
35
|
+
expect(collapsedNodeWidth("a".repeat(100))).toBe(240);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("scales with label length between the bounds", () => {
|
|
39
|
+
const width = collapsedNodeWidth("df, summary");
|
|
40
|
+
expect(width).toBeGreaterThan(90);
|
|
41
|
+
expect(width).toBeLessThan(240);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
describe("isStateFlowVariable", () => {
|
|
46
|
+
it("flags State and SetFunctor (from mo.state)", () => {
|
|
47
|
+
expect(
|
|
48
|
+
isStateFlowVariable(variable({ name: "g", dataType: "State" })),
|
|
49
|
+
).toBe(true);
|
|
50
|
+
expect(
|
|
51
|
+
isStateFlowVariable(variable({ name: "s", dataType: "SetFunctor" })),
|
|
52
|
+
).toBe(true);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("does not flag UI elements or plain values", () => {
|
|
56
|
+
// UIElement is frontend-driven / immutable during a run — not a state flow.
|
|
57
|
+
expect(
|
|
58
|
+
isStateFlowVariable(variable({ name: "sl", dataType: "slider" })),
|
|
59
|
+
).toBe(false);
|
|
60
|
+
expect(isStateFlowVariable(variable({ name: "n", dataType: "int" }))).toBe(
|
|
61
|
+
false,
|
|
62
|
+
);
|
|
63
|
+
expect(isStateFlowVariable(variable({ name: "u", dataType: null }))).toBe(
|
|
64
|
+
false,
|
|
65
|
+
);
|
|
66
|
+
expect(isStateFlowVariable(variable({ name: "u2" }))).toBe(false);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe("wrapDefs", () => {
|
|
71
|
+
it("packs short names to the 12-char minimum budget", () => {
|
|
72
|
+
expect(
|
|
73
|
+
wrapDefs(names(["np", "math", "time", "io", "numpy", "pandas"])),
|
|
74
|
+
).toEqual(["np, math", "time, io", "numpy", "pandas"]);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("gives a long name its own line, then packs the rest to its width", () => {
|
|
78
|
+
expect(
|
|
79
|
+
wrapDefs(
|
|
80
|
+
names([
|
|
81
|
+
"very_very_very_long_variable_name",
|
|
82
|
+
"short_var",
|
|
83
|
+
"shr_v",
|
|
84
|
+
"io",
|
|
85
|
+
"numpy",
|
|
86
|
+
"wev",
|
|
87
|
+
]),
|
|
88
|
+
),
|
|
89
|
+
).toEqual([
|
|
90
|
+
"very_very_very_long_variable_name",
|
|
91
|
+
"short_var, shr_v, io, numpy, wev",
|
|
92
|
+
]);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("returns an empty list for no defs", () => {
|
|
96
|
+
expect(wrapDefs(names([]))).toEqual([]);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("caps the budget so packed lines fit the max node width", () => {
|
|
100
|
+
// One name far beyond the width cap must not widen every other line.
|
|
101
|
+
const huge = "x".repeat(60);
|
|
102
|
+
const lines = wrapDefs(
|
|
103
|
+
names([huge, "aaaaaaaaaa", "bbbbbbbbbb", "cccccccccc"]),
|
|
104
|
+
);
|
|
105
|
+
expect(lines[0]).toBe(huge);
|
|
106
|
+
for (const line of lines.slice(1)) {
|
|
107
|
+
expect(line.length).toBeLessThanOrEqual(33);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
describe("computeDefsByCell", () => {
|
|
113
|
+
it("maps each cell to the variables it declares", () => {
|
|
114
|
+
const c1 = "c1" as CellId;
|
|
115
|
+
const c2 = "c2" as CellId;
|
|
116
|
+
const a = "a" as VariableName;
|
|
117
|
+
const b = "b" as VariableName;
|
|
118
|
+
const c = "c" as VariableName;
|
|
119
|
+
const variables: Variables = {
|
|
120
|
+
[a]: variable({ name: a, declaredBy: [c1], usedBy: [c2] }),
|
|
121
|
+
[b]: variable({ name: b, declaredBy: [c1] }),
|
|
122
|
+
[c]: variable({ name: c, declaredBy: [c2] }),
|
|
123
|
+
};
|
|
124
|
+
const defs = computeDefsByCell(variables);
|
|
125
|
+
expect(defs.get(c1)).toEqual([a, b]);
|
|
126
|
+
expect(defs.get(c2)).toEqual([c]);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("returns an empty map when there are no variables", () => {
|
|
130
|
+
expect(computeDefsByCell({}).size).toBe(0);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
describe("reusableCellIds", () => {
|
|
135
|
+
const c1 = "c1" as CellId;
|
|
136
|
+
const c2 = "c2" as CellId;
|
|
137
|
+
const c3 = "c3" as CellId;
|
|
138
|
+
|
|
139
|
+
it("collects cells whose serialization hint is valid, case-insensitively", () => {
|
|
140
|
+
const runtime = {
|
|
141
|
+
[c1]: createCellRuntimeState({ serialization: "Valid" }),
|
|
142
|
+
[c2]: createCellRuntimeState({ serialization: "Unparsable" }),
|
|
143
|
+
[c3]: createCellRuntimeState(),
|
|
144
|
+
};
|
|
145
|
+
expect(reusableCellIds([c1, c2, c3], runtime)).toEqual(new Set([c1]));
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it("ignores cells with no runtime entry", () => {
|
|
149
|
+
expect(reusableCellIds([c1], {})).toEqual(new Set());
|
|
150
|
+
});
|
|
151
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { memo } from "react";
|
|
4
|
+
import { BaseEdge, type EdgeProps, getBezierPath } from "reactflow";
|
|
5
|
+
import {
|
|
6
|
+
DEFAULT_EDGE_COLOR,
|
|
7
|
+
type DependencyEdgeData,
|
|
8
|
+
STATE_FLOW_EDGE_COLOR,
|
|
9
|
+
} from "./elements";
|
|
10
|
+
|
|
11
|
+
// Gentle curvature keeps edges reading as clean directional splines
|
|
12
|
+
// (TensorBoard-style) rather than hard right-angle elbows.
|
|
13
|
+
const EDGE_CURVATURE = 0.35;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* A dependency edge, drawn as a smooth curved spline that flows out of the
|
|
17
|
+
* source handle and into the target — the TensorBoard graph aesthetic. Mutable
|
|
18
|
+
* `State`/setter flows get a distinct dashed accent style.
|
|
19
|
+
*/
|
|
20
|
+
export const DependencyEdge = memo((props: EdgeProps<DependencyEdgeData>) => {
|
|
21
|
+
const {
|
|
22
|
+
sourceX,
|
|
23
|
+
sourceY,
|
|
24
|
+
targetX,
|
|
25
|
+
targetY,
|
|
26
|
+
sourcePosition,
|
|
27
|
+
targetPosition,
|
|
28
|
+
markerEnd,
|
|
29
|
+
data,
|
|
30
|
+
selected,
|
|
31
|
+
} = props;
|
|
32
|
+
|
|
33
|
+
const [path] = getBezierPath({
|
|
34
|
+
sourceX,
|
|
35
|
+
sourceY,
|
|
36
|
+
targetX,
|
|
37
|
+
targetY,
|
|
38
|
+
sourcePosition,
|
|
39
|
+
targetPosition,
|
|
40
|
+
curvature: EDGE_CURVATURE,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const isStateFlow = data?.isStateFlow ?? false;
|
|
44
|
+
// The inline stroke replaces React Flow's stylesheet-driven edge colors, so
|
|
45
|
+
// the selected state must be restyled here too or it becomes invisible.
|
|
46
|
+
const stroke = selected
|
|
47
|
+
? "var(--primary)"
|
|
48
|
+
: isStateFlow
|
|
49
|
+
? STATE_FLOW_EDGE_COLOR
|
|
50
|
+
: DEFAULT_EDGE_COLOR;
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<BaseEdge
|
|
54
|
+
path={path}
|
|
55
|
+
markerEnd={markerEnd}
|
|
56
|
+
style={{
|
|
57
|
+
strokeWidth: selected ? 2.5 : isStateFlow ? 2 : 1.5,
|
|
58
|
+
stroke,
|
|
59
|
+
strokeDasharray: isStateFlow ? "5 3" : undefined,
|
|
60
|
+
}}
|
|
61
|
+
/>
|
|
62
|
+
);
|
|
63
|
+
});
|
|
64
|
+
DependencyEdge.displayName = "DependencyEdge";
|
|
65
|
+
|
|
66
|
+
export const edgeTypes = {
|
|
67
|
+
dependency: DependencyEdge,
|
|
68
|
+
};
|
|
@@ -2,25 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
import { useAtomValue } from "jotai";
|
|
4
4
|
import React, { memo, use } from "react";
|
|
5
|
-
import { Handle, Position
|
|
5
|
+
import { Handle, Position } from "reactflow";
|
|
6
6
|
import { TinyCode } from "@/components/editor/cell/TinyCode";
|
|
7
7
|
import { useCellIds } from "@/core/cells/cells";
|
|
8
|
-
import { displayCellName } from "@/core/cells/names";
|
|
8
|
+
import { displayCellName, isInternalCellName } from "@/core/cells/names";
|
|
9
9
|
import { cn } from "@/utils/cn";
|
|
10
10
|
import {
|
|
11
11
|
type CustomNodeProps,
|
|
12
|
-
getNodeHeight,
|
|
13
12
|
INPUTS_HANDLE_ID,
|
|
13
|
+
nodeBodyLines,
|
|
14
|
+
nodeDimensions,
|
|
14
15
|
OUTPUTS_HANDLE_ID,
|
|
15
16
|
} from "./elements";
|
|
16
17
|
import type { LayoutDirection } from "./types";
|
|
17
|
-
|
|
18
|
-
function getWidth(canvasWidth: number) {
|
|
19
|
-
const minWidth = 100;
|
|
20
|
-
const maxWidth = 400;
|
|
21
|
-
const padding = 50;
|
|
22
|
-
return Math.min(Math.max(canvasWidth - padding * 2, minWidth), maxWidth);
|
|
23
|
-
}
|
|
18
|
+
import { extractCellPreview } from "./utils/cell-preview";
|
|
24
19
|
|
|
25
20
|
export const EdgeMarkerContext = React.createContext<LayoutDirection>("LR");
|
|
26
21
|
|
|
@@ -36,13 +31,19 @@ export const CustomNode = memo((props: CustomNodeProps) => {
|
|
|
36
31
|
const { data, selected } = props; // must match the equality check
|
|
37
32
|
const cell = useAtomValue(data.atom);
|
|
38
33
|
const cellIndex = useCellIds().inOrderIds.indexOf(cell.id);
|
|
39
|
-
const
|
|
40
|
-
const selectedColor = "var(--gray-9)";
|
|
41
|
-
const color = selected ? selectedColor : nonSelectedColor;
|
|
42
|
-
const reactFlowWidth = useStore(({ width }) => width);
|
|
34
|
+
const handleColor = selected ? "var(--gray-9)" : "var(--gray-3)";
|
|
43
35
|
const edgeMarkers = use(EdgeMarkerContext);
|
|
44
36
|
|
|
45
|
-
const
|
|
37
|
+
const { width, height } = nodeDimensions(data);
|
|
38
|
+
const isNamed = !isInternalCellName(cell.name);
|
|
39
|
+
const name = displayCellName(cell.name, cellIndex);
|
|
40
|
+
// Only collapsed nodes without defs render the preview; skip the parse
|
|
41
|
+
// otherwise, since it scales with notebook size.
|
|
42
|
+
const preview =
|
|
43
|
+
!data.expanded && data.defs.length === 0
|
|
44
|
+
? extractCellPreview(cell.code).text
|
|
45
|
+
: undefined;
|
|
46
|
+
|
|
46
47
|
return (
|
|
47
48
|
<div>
|
|
48
49
|
<Handle
|
|
@@ -50,43 +51,70 @@ export const CustomNode = memo((props: CustomNodeProps) => {
|
|
|
50
51
|
id={INPUTS_HANDLE_ID}
|
|
51
52
|
data-testid="input-one"
|
|
52
53
|
position={edgeMarkers === "LR" ? Position.Left : Position.Top}
|
|
53
|
-
style={{ background:
|
|
54
|
+
style={{ background: handleColor }}
|
|
54
55
|
/>
|
|
55
56
|
<Handle
|
|
56
57
|
type="source"
|
|
57
58
|
id={INPUTS_HANDLE_ID}
|
|
58
59
|
data-testid="input-two"
|
|
59
60
|
position={edgeMarkers === "LR" ? Position.Left : Position.Top}
|
|
60
|
-
style={{ background:
|
|
61
|
+
style={{ background: handleColor }}
|
|
61
62
|
/>
|
|
62
63
|
<div
|
|
63
64
|
className={cn(
|
|
64
65
|
"flex flex-col bg-card border border-input/50 rounded-md mx-[2px] overflow-hidden",
|
|
65
66
|
selected && "border-primary",
|
|
66
67
|
)}
|
|
67
|
-
style={{
|
|
68
|
-
height: getNodeHeight(linesOfCode),
|
|
69
|
-
width: data.forceWidth || getWidth(reactFlowWidth),
|
|
70
|
-
}}
|
|
68
|
+
style={{ height, width }}
|
|
71
69
|
>
|
|
72
|
-
<div className="text-
|
|
73
|
-
|
|
70
|
+
<div className="text-xs py-0.5 px-2 bg-muted border-b flex items-center justify-between gap-2 shrink-0">
|
|
71
|
+
<span
|
|
72
|
+
className={cn(
|
|
73
|
+
"truncate",
|
|
74
|
+
isNamed
|
|
75
|
+
? "font-semibold text-foreground"
|
|
76
|
+
: "text-muted-foreground/70",
|
|
77
|
+
)}
|
|
78
|
+
>
|
|
79
|
+
{name}
|
|
80
|
+
</span>
|
|
74
81
|
</div>
|
|
75
|
-
|
|
82
|
+
{data.expanded ? (
|
|
83
|
+
<TinyCode code={cell.code} />
|
|
84
|
+
) : (
|
|
85
|
+
<div className="flex-1 min-h-0 flex flex-col justify-center px-2 py-1 font-mono text-xs leading-4">
|
|
86
|
+
{data.defs.length > 0 ? (
|
|
87
|
+
// Defs wrapped into comma-separated lines — taller, not wider.
|
|
88
|
+
nodeBodyLines(data).map((line) => (
|
|
89
|
+
<span
|
|
90
|
+
key={line}
|
|
91
|
+
className="text-foreground truncate"
|
|
92
|
+
title={line}
|
|
93
|
+
>
|
|
94
|
+
{line}
|
|
95
|
+
</span>
|
|
96
|
+
))
|
|
97
|
+
) : (
|
|
98
|
+
<span className="truncate italic text-muted-foreground/60">
|
|
99
|
+
{preview ?? "empty"}
|
|
100
|
+
</span>
|
|
101
|
+
)}
|
|
102
|
+
</div>
|
|
103
|
+
)}
|
|
76
104
|
</div>
|
|
77
105
|
<Handle
|
|
78
106
|
type="source"
|
|
79
107
|
id={OUTPUTS_HANDLE_ID}
|
|
80
108
|
data-testid="output-one"
|
|
81
109
|
position={edgeMarkers === "LR" ? Position.Right : Position.Bottom}
|
|
82
|
-
style={{ background:
|
|
110
|
+
style={{ background: handleColor }}
|
|
83
111
|
/>
|
|
84
112
|
<Handle
|
|
85
113
|
type="target"
|
|
86
114
|
id={OUTPUTS_HANDLE_ID}
|
|
87
115
|
data-testid="output-two"
|
|
88
116
|
position={edgeMarkers === "LR" ? Position.Right : Position.Bottom}
|
|
89
|
-
style={{ background:
|
|
117
|
+
style={{ background: handleColor }}
|
|
90
118
|
/>
|
|
91
119
|
</div>
|
|
92
120
|
);
|