@marimo-team/islands 0.24.3-dev10 → 0.24.3-dev14
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/{chat-ui-BSwr409J.js → chat-ui-CH7Gg-KH.js} +2 -2
- package/dist/{common-SEaoDOh7.js → common-D5xpkGya.js} +2 -2
- package/dist/{html-to-image-BtFMe_Nq.js → html-to-image-B3oyXfx7.js} +7 -6
- package/dist/main.js +30 -21
- package/dist/{process-output-CPy0YKUe.js → process-output-ew3B35of.js} +1 -1
- package/dist/{reveal-component-Cun9aLyZ.js → reveal-component-BrRhgqQO.js} +2 -2
- package/package.json +1 -1
- package/src/components/data-table/column-explorer-panel/column-explorer.tsx +2 -6
- package/src/components/editor/chrome/components/feedback-button.tsx +10 -9
- package/src/components/editor/code/__tests__/readonly-diff.test.tsx +65 -0
- package/src/components/editor/code/readonly-diff.tsx +1 -1
- package/src/components/editor/file-tree/__tests__/tree-actions.test.tsx +54 -0
- package/src/components/editor/file-tree/tree-actions.tsx +11 -14
- package/src/core/cells/pending-delete-service.ts +12 -16
- package/src/core/codemirror/language/__tests__/sql.test.ts +70 -2
- package/src/core/codemirror/language/languages/sql/sql.ts +16 -1
- package/src/core/datasets/__tests__/data-source.test.ts +2 -0
- package/src/core/datasets/data-source-connections.ts +15 -10
- package/src/plugins/layout/AccordionPlugin.tsx +7 -2
- package/src/plugins/layout/__test__/AccordionPlugin.test.tsx +66 -0
|
@@ -3,12 +3,12 @@ import { c as n, g as r, h as i, t as a } from "./button-CKBkqR9J.js";
|
|
|
3
3
|
import { t as o } from "./react-DMWcHin-.js";
|
|
4
4
|
import { t as s } from "./compiler-runtime-CAH9RLw2.js";
|
|
5
5
|
import { r as c } from "./copy-CD7S58CY.js";
|
|
6
|
-
import { Cn as l, F as u, Ft as d, Gt as f, Hn as p, In as m, Pn as h, Qn as g, Un as _, Vt as v, Wt as y, Xt as b, Yn as x, Zn as S, _ as C, a as w, c as T, d as E, er as ee, f as D, g as te, gn as ne, gt as re, h as ie, hn as ae, ht as oe, in as se, l as ce, m as le, mn as ue, n as de, nn as fe, on as pe, p as me, pn as he, pt as ge, q as _e, r as ve, rn as ye, rr as be, rt as xe, t as Se, v as Ce, vn as we, x as Te, y as Ee, yt as De, zt as Oe } from "./html-to-image-
|
|
6
|
+
import { Cn as l, F as u, Ft as d, Gt as f, Hn as p, In as m, Pn as h, Qn as g, Un as _, Vt as v, Wt as y, Xt as b, Yn as x, Zn as S, _ as C, a as w, c as T, d as E, er as ee, f as D, g as te, gn as ne, gt as re, h as ie, hn as ae, ht as oe, in as se, l as ce, m as le, mn as ue, n as de, nn as fe, on as pe, p as me, pn as he, pt as ge, q as _e, r as ve, rn as ye, rr as be, rt as xe, t as Se, v as Ce, vn as we, x as Te, y as Ee, yt as De, zt as Oe } from "./html-to-image-B3oyXfx7.js";
|
|
7
7
|
import { u as ke } from "./dist-3_o3T78I.js";
|
|
8
8
|
import { C as Ae, I as je, R as Me, b as Ne } from "./select-3qHpFKF9.js";
|
|
9
9
|
import { _ as Pe, f as Fe, h as Ie, i as Le, n as Re, t as ze } from "./dropdown-menu-DhCfcscX.js";
|
|
10
10
|
import { p as Be, v as Ve } from "./toDate-CpXBeDq0.js";
|
|
11
|
-
import { d as He, f as Ue, i as We, n as Ge, t as Ke, u as qe } from "./process-output-
|
|
11
|
+
import { d as He, f as Ue, i as We, n as Ge, t as Ke, u as qe } from "./process-output-ew3B35of.js";
|
|
12
12
|
import { t as Je } from "./jsx-runtime-CpoKNRgu.js";
|
|
13
13
|
import { Cn as O, Cr as Ye, In as k, It as Xe, Jn as A, Sn as Ze, Tt as Qe, Yn as $e, bt as et, cn as tt, jn as nt, kt as j, ln as M, mr as rt, un as N, vt as it, wt as at, xt as ot, zt as st } from "./schemas-DzK6U94N.js";
|
|
14
14
|
import { n as ct } from "./error-banner-CsXbEEjo.js";
|
|
@@ -3,7 +3,7 @@ import { _ as n, c as r, f as i, g as a, h as o, m as s, n as c, p as l, r as u,
|
|
|
3
3
|
import { t as f } from "./react-DMWcHin-.js";
|
|
4
4
|
import { t as p } from "./compiler-runtime-CAH9RLw2.js";
|
|
5
5
|
import { n as m, r as h, t as g } from "./copy-CD7S58CY.js";
|
|
6
|
-
import { $ as _, $n as v, $t as y, A as b, An as x, At as S, Cn as C, Ct as w, D as T, E, Et as D, F as O, Gn as k, H as A, Hn as j, Ht as M, Jn as N, Kt as P, Ln as F, M as I, Mn as L, N as R, Nt as ee, Pn as z, Q as te, Qn as ne, Qt as re, Rt as ie, S as ae, T as oe, Tt as se, Un as ce, Ut as le, V as ue, Wt as de, Xt as fe, Yn as pe, Z as me, Zn as he, _ as ge, _t as _e, ar as ve, cn as ye, en as be, er as xe, g as Se, gt as Ce, ht as we, it as Te, jn as Ee, jt as De, kt as Oe, lt as ke, mt as Ae, n as je, on as Me, or as Ne, ot as Pe, pt as Fe, q as Ie, qn as Le, r as Re, rr as ze, s as Be, sr as Ve, t as He, tt as Ue, u as We, v as Ge, vt as Ke, w as qe, wn as Je, y as Ye, yn as Xe, yt as Ze } from "./html-to-image-
|
|
6
|
+
import { $ as _, $n as v, $t as y, A as b, An as x, At as S, Cn as C, Ct as w, D as T, E, Et as D, F as O, Gn as k, H as A, Hn as j, Ht as M, Jn as N, Kt as P, Ln as F, M as I, Mn as L, N as R, Nt as ee, Pn as z, Q as te, Qn as ne, Qt as re, Rt as ie, S as ae, T as oe, Tt as se, Un as ce, Ut as le, V as ue, Wt as de, Xt as fe, Yn as pe, Z as me, Zn as he, _ as ge, _t as _e, ar as ve, cn as ye, en as be, er as xe, g as Se, gt as Ce, ht as we, it as Te, jn as Ee, jt as De, kt as Oe, lt as ke, mt as Ae, n as je, on as Me, or as Ne, ot as Pe, pt as Fe, q as Ie, qn as Le, r as Re, rr as ze, s as Be, sr as Ve, t as He, tt as Ue, u as We, v as Ge, vt as Ke, w as qe, wn as Je, y as Ye, yn as Xe, yt as Ze } from "./html-to-image-B3oyXfx7.js";
|
|
7
7
|
import { o as Qe, u as B } from "./dist-3_o3T78I.js";
|
|
8
8
|
import { c as $e, i as et, r as tt } from "./useLifecycle-B4RGlyf0.js";
|
|
9
9
|
import { a as nt, i as rt, n as it, o as at, r as ot, t as st } from "./useDateFormatter-Cp9Jdxls.js";
|
|
@@ -32164,7 +32164,7 @@ function XK() {
|
|
|
32164
32164
|
return a.warn("Failed to get version from mount config"), null;
|
|
32165
32165
|
}
|
|
32166
32166
|
}
|
|
32167
|
-
var ZK = XK() || "0.24.3-
|
|
32167
|
+
var ZK = XK() || "0.24.3-dev14", QK = V(ZK), $K = V(!0);
|
|
32168
32168
|
V(null);
|
|
32169
32169
|
//#endregion
|
|
32170
32170
|
//#region src/core/wasm/state.ts
|
|
@@ -10454,7 +10454,7 @@ function If(e) {
|
|
|
10454
10454
|
}
|
|
10455
10455
|
function Lf() {
|
|
10456
10456
|
return {
|
|
10457
|
-
latestEngineSelected:
|
|
10457
|
+
latestEngineSelected: null,
|
|
10458
10458
|
connectionsMap: Pf
|
|
10459
10459
|
};
|
|
10460
10460
|
}
|
|
@@ -10462,7 +10462,7 @@ var { reducer: Rf, createActions: zf, valueAtom: Bf, useActions: Vf } = D(Lf, {
|
|
|
10462
10462
|
addDataSourceConnection: (e, t) => {
|
|
10463
10463
|
if (t.connections.length === 0) return e;
|
|
10464
10464
|
let { latestEngineSelected: n, connectionsMap: r } = e, i = new Map(r);
|
|
10465
|
-
for (let e of t.connections) i.set(e.name, e);
|
|
10465
|
+
for (let e of t.connections) i.delete(e.name), i.set(e.name, e);
|
|
10466
10466
|
return {
|
|
10467
10467
|
latestEngineSelected: n,
|
|
10468
10468
|
connectionsMap: i
|
|
@@ -10471,18 +10471,18 @@ var { reducer: Rf, createActions: zf, valueAtom: Bf, useActions: Vf } = D(Lf, {
|
|
|
10471
10471
|
filterDataSourcesFromVariables: (e, t) => {
|
|
10472
10472
|
let { latestEngineSelected: n, connectionsMap: r } = e, i = new Set(t), a = new Map([...r].filter(([e]) => Tf.has(e) ? !0 : i.has(e)));
|
|
10473
10473
|
return {
|
|
10474
|
-
latestEngineSelected: a.has(n) ? n :
|
|
10474
|
+
latestEngineSelected: n && a.has(n) ? n : null,
|
|
10475
10475
|
connectionsMap: a
|
|
10476
10476
|
};
|
|
10477
10477
|
},
|
|
10478
10478
|
clearDataSourceConnections: () => ({
|
|
10479
|
-
latestEngineSelected:
|
|
10479
|
+
latestEngineSelected: null,
|
|
10480
10480
|
connectionsMap: /* @__PURE__ */ new Map()
|
|
10481
10481
|
}),
|
|
10482
10482
|
removeDataSourceConnection: (e, t) => {
|
|
10483
10483
|
let { latestEngineSelected: n, connectionsMap: r } = e, i = new Map(r);
|
|
10484
10484
|
return i.delete(t), {
|
|
10485
|
-
latestEngineSelected: i.has(n) ? n :
|
|
10485
|
+
latestEngineSelected: n && i.has(n) ? n : null,
|
|
10486
10486
|
connectionsMap: i
|
|
10487
10487
|
};
|
|
10488
10488
|
},
|
|
@@ -26251,7 +26251,8 @@ function lx() {
|
|
|
26251
26251
|
//#region src/core/codemirror/language/languages/sql/sql.ts
|
|
26252
26252
|
var ux = vf, dx = "DuckDB", fx = new Ft();
|
|
26253
26253
|
function px() {
|
|
26254
|
-
|
|
26254
|
+
let { latestEngineSelected: e, connectionsMap: t } = F.get(Bf);
|
|
26255
|
+
return e === null ? [...t.values()].reverse().find((e) => !Tf.has(e.name) && e.source !== "iceberg")?.name ?? "__marimo_duckdb" : e;
|
|
26255
26256
|
}
|
|
26256
26257
|
var mx = class {
|
|
26257
26258
|
parser = new Em();
|
package/dist/main.js
CHANGED
|
@@ -3,16 +3,16 @@ import { _ as a, c as o, f as s, g as c, h as l, i as u, l as d, m as f, n as p,
|
|
|
3
3
|
import { t as y } from "./react-DMWcHin-.js";
|
|
4
4
|
import { t as b } from "./compiler-runtime-CAH9RLw2.js";
|
|
5
5
|
import { n as x, r as S, t as C } from "./copy-CD7S58CY.js";
|
|
6
|
-
import { $n as w, An as T, B as E, Bn as D, Bt as O, C as k, Ct as A, Dn as j, Dt as M, En as N, F as P, Fn as F, G as I, I as L, It as ee, J as te, Jt as R, K as ne, Kn as z, L as re, Lt as ie, Mt as ae, Nn as oe, Nt as se, On as ce, Ot as le, P as ue, Pn as de, Pt as fe, Q as pe, Qn as me, Qt as he, R as ge, Rn as _e, Sn as ve, St as ye, U as be, Un as xe, Vn as Se, Vt as Ce, W as we, Wn as Te, Wt as Ee, X as De, Xn as Oe, Y as ke, Yn as Ae, Yt as je, Z as Me, Zn as Ne, Zt as Pe, _ as Fe, _n as Ie, a as Le, an as Re, ar as ze, at as Be, b as Ve, bn as He, bt as Ue, cn as We, ct as Ge, dn as Ke, dt as qe, et as Je, fn as Ye, ft as Xe, g as Ze, i as Qe, ir as $e, j as et, jt as tt, k as nt, kn as rt, kt as it, ln as at, n as ot, nr as st, o as ct, on as lt, ot as ut, q as dt, qt as ft, sn as pt, st as mt, tn as ht, tr as gt, un as _t, ut as vt, v as yt, wn as bt, wt as xt, xn as St, xt as Ct, y as wt, z as Tt, zn as Et } from "./html-to-image-
|
|
6
|
+
import { $n as w, An as T, B as E, Bn as D, Bt as O, C as k, Ct as A, Dn as j, Dt as M, En as N, F as P, Fn as F, G as I, I as L, It as ee, J as te, Jt as R, K as ne, Kn as z, L as re, Lt as ie, Mt as ae, Nn as oe, Nt as se, On as ce, Ot as le, P as ue, Pn as de, Pt as fe, Q as pe, Qn as me, Qt as he, R as ge, Rn as _e, Sn as ve, St as ye, U as be, Un as xe, Vn as Se, Vt as Ce, W as we, Wn as Te, Wt as Ee, X as De, Xn as Oe, Y as ke, Yn as Ae, Yt as je, Z as Me, Zn as Ne, Zt as Pe, _ as Fe, _n as Ie, a as Le, an as Re, ar as ze, at as Be, b as Ve, bn as He, bt as Ue, cn as We, ct as Ge, dn as Ke, dt as qe, et as Je, fn as Ye, ft as Xe, g as Ze, i as Qe, ir as $e, j as et, jt as tt, k as nt, kn as rt, kt as it, ln as at, n as ot, nr as st, o as ct, on as lt, ot as ut, q as dt, qt as ft, sn as pt, st as mt, tn as ht, tr as gt, un as _t, ut as vt, v as yt, wn as bt, wt as xt, xn as St, xt as Ct, y as wt, z as Tt, zn as Et } from "./html-to-image-B3oyXfx7.js";
|
|
7
7
|
import { o as Dt, s as Ot, u as kt } from "./dist-3_o3T78I.js";
|
|
8
8
|
import { D as At, E as jt, O as Mt, S as Nt, T as Pt, _ as Ft, a as It, c as Lt, d as Rt, f as zt, g as Bt, h as Vt, i as Ht, l as Ut, m as Wt, n as Gt, o as Kt, p as qt, r as Jt, s as Yt, t as Xt, u as Zt, v as Qt, w as $t, x as en, y as tn } from "./spec-CutWdqo3.js";
|
|
9
|
-
import { $ as nn, $t as rn, A as an, At as on, B as sn, Bt as cn, C as ln, Cn as un, Ct as dn, D as fn, Dn as pn, Dt as mn, E as hn, En as gn, Et as _n, F as vn, Ft as yn, G as bn, Gt as xn, H as Sn, Ht as Cn, I as wn, It as Tn, J as En, Jt as Dn, K as On, Kt as kn, L as An, Lt as jn, M as Mn, N as Nn, Nt as Pn, O as Fn, Ot as In, P as Ln, Pt as Rn, Q as zn, Qt as Bn, R as Vn, Rt as Hn, S as Un, Sn as Wn, St as Gn, T as Kn, Tn as qn, Tt as Jn, U as Yn, Ut as Xn, V as Zn, Vt as Qn, W as $n, Wt as er, X as tr, Xt as nr, Y as rr, Yt as ir, Z as ar, Zt as or, _ as sr, _n as cr, _t as lr, a as ur, an as dr, at as fr, b as pr, bn as mr, bt as hr, cn as gr, dn as _r, dt as vr, en as yr, et as br, fn as xr, ft as Sr, g as Cr, gn as wr, gt as Tr, hn as Er, ht as Dr, i as Or, in as kr, it as Ar, j as jr, k as Mr, kt as Nr, l as Pr, ln as Fr, m as Ir, mn as Lr, mt as Rr, n as zr, nn as Br, nt as Vr, on as Hr, ot as Ur, pn as Wr, pt as Gr, q as Kr, qt as qr, rn as Jr, rt as Yr, s as Xr, sn as Zr, st as Qr, t as $r, tn as ei, tt as ti, un as ni, v as ri, vt as ii, w as ai, wn as oi, wt as si, x as ci, xn as li, xt as ui, y as di, yn as fi, yt as pi, z as mi, zt as hi } from "./common-
|
|
9
|
+
import { $ as nn, $t as rn, A as an, At as on, B as sn, Bt as cn, C as ln, Cn as un, Ct as dn, D as fn, Dn as pn, Dt as mn, E as hn, En as gn, Et as _n, F as vn, Ft as yn, G as bn, Gt as xn, H as Sn, Ht as Cn, I as wn, It as Tn, J as En, Jt as Dn, K as On, Kt as kn, L as An, Lt as jn, M as Mn, N as Nn, Nt as Pn, O as Fn, Ot as In, P as Ln, Pt as Rn, Q as zn, Qt as Bn, R as Vn, Rt as Hn, S as Un, Sn as Wn, St as Gn, T as Kn, Tn as qn, Tt as Jn, U as Yn, Ut as Xn, V as Zn, Vt as Qn, W as $n, Wt as er, X as tr, Xt as nr, Y as rr, Yt as ir, Z as ar, Zt as or, _ as sr, _n as cr, _t as lr, a as ur, an as dr, at as fr, b as pr, bn as mr, bt as hr, cn as gr, dn as _r, dt as vr, en as yr, et as br, fn as xr, ft as Sr, g as Cr, gn as wr, gt as Tr, hn as Er, ht as Dr, i as Or, in as kr, it as Ar, j as jr, k as Mr, kt as Nr, l as Pr, ln as Fr, m as Ir, mn as Lr, mt as Rr, n as zr, nn as Br, nt as Vr, on as Hr, ot as Ur, pn as Wr, pt as Gr, q as Kr, qt as qr, rn as Jr, rt as Yr, s as Xr, sn as Zr, st as Qr, t as $r, tn as ei, tt as ti, un as ni, v as ri, vt as ii, w as ai, wn as oi, wt as si, x as ci, xn as li, xt as ui, y as di, yn as fi, yt as pi, z as mi, zt as hi } from "./common-D5xpkGya.js";
|
|
10
10
|
import { c as gi, i as _i, n as vi, o as yi, t as bi } from "./useLifecycle-B4RGlyf0.js";
|
|
11
11
|
import { t as xi } from "./check-CHig47QV.js";
|
|
12
12
|
import { A as Si, C as Ci, D as wi, E as Ti, F as Ei, I as Di, M as Oi, N as ki, P as Ai, R as ji, S as Mi, a as Ni, b as Pi, c as Fi, i as Ii, j as Li, l as Ri, n as zi, o as Bi, r as Vi, s as Hi, t as Ui, w as Wi, x as Gi } from "./select-3qHpFKF9.js";
|
|
13
13
|
import { B as Ki, C as qi, Ft as Ji, J as Yi, Pt as Xi, S as Zi, Ut as Qi, W as $i, Y as ea, _ as ta, a as na, at as ra, bt as ia, c as aa, f as oa, ft as sa, g as ca, h as la, hn as ua, i as da, m as fa, mn as pa, n as ma, ot as ha, p as ga, r as _a, s as va, t as ya, v as ba, x as xa, z as Sa } from "./dropdown-menu-DhCfcscX.js";
|
|
14
14
|
import { _ as Ca, c as wa, d as Ta, f as Ea, g as Da, h as Oa, m as ka, u as Aa, v as ja } from "./toDate-CpXBeDq0.js";
|
|
15
|
-
import { a as Ma, c as Na, d as Pa, f as Fa, i as Ia, l as La, n as Ra, o as za, r as Ba, s as Va, t as Ha } from "./process-output-
|
|
15
|
+
import { a as Ma, c as Na, d as Pa, f as Fa, i as Ia, l as La, n as Ra, o as za, r as Ba, s as Va, t as Ha } from "./process-output-ew3B35of.js";
|
|
16
16
|
import { a as Ua, c as Wa, d as Ga, i as Ka, l as qa, r as Ja, u as Ya } from "./glide-utils-NShAPPtn.js";
|
|
17
17
|
import { n as Xa, t as Za } from "./ErrorBoundary-CR-ZQN_c.js";
|
|
18
18
|
import { t as Qa } from "./react-dom-C94wmq5w.js";
|
|
@@ -15392,7 +15392,7 @@ var CM = (e) => {
|
|
|
15392
15392
|
output: t
|
|
15393
15393
|
};
|
|
15394
15394
|
} };
|
|
15395
|
-
} }, kM = q.lazy(() => import("./chat-ui-
|
|
15395
|
+
} }, kM = q.lazy(() => import("./chat-ui-CH7Gg-KH.js").then((e) => ({ default: e.Chatbot }))), AM = U(B({
|
|
15396
15396
|
id: V(),
|
|
15397
15397
|
role: Qp([
|
|
15398
15398
|
"system",
|
|
@@ -19391,7 +19391,7 @@ var eI = (e) => {
|
|
|
19391
19391
|
//#endregion
|
|
19392
19392
|
//#region src/components/data-table/column-explorer-panel/column-explorer.tsx
|
|
19393
19393
|
function tI({ previewColumn: e, fieldTypes: t, totalRows: n, totalColumns: r, tableId: i, table: a }) {
|
|
19394
|
-
let [o, s] = (0, q.useState)(""), { locale: c } = Ti(), l = t?.filter(([e]) => !(e === "__select__" || e === "_marimo_row_id" || e.startsWith("__m_column__"))), u = l?.filter(([e]) => _r(o, e)), { totalColumns: d, hiddenColumns: f } = $n(a, r), { visible: p } = On(a), m =
|
|
19394
|
+
let [o, s] = (0, q.useState)(""), { locale: c } = Ti(), l = t?.filter(([e]) => !(e === "__select__" || e === "_marimo_row_id" || e.startsWith("__m_column__"))), u = l?.filter(([e]) => _r(o, e)), { totalColumns: d, hiddenColumns: f } = $n(a, r), { visible: p } = On(a), m = bn(a), { rowsAndColumns: h, hiddenSuffix: g } = Sn({
|
|
19395
19395
|
numRows: n,
|
|
19396
19396
|
totalColumns: d,
|
|
19397
19397
|
locale: c,
|
|
@@ -19402,8 +19402,8 @@ function tI({ previewColumn: e, fieldTypes: t, totalRows: n, totalColumns: r, ta
|
|
|
19402
19402
|
children: [/* @__PURE__ */ (0, J.jsxs)("div", {
|
|
19403
19403
|
className: "text-xs font-semibold ml-2 flex items-center gap-1",
|
|
19404
19404
|
children: [
|
|
19405
|
-
|
|
19406
|
-
f > 0 && /* @__PURE__ */ (0, J.jsx)("span", { children:
|
|
19405
|
+
h,
|
|
19406
|
+
f > 0 && /* @__PURE__ */ (0, J.jsx)("span", { children: g }),
|
|
19407
19407
|
/* @__PURE__ */ (0, J.jsx)(Wm, {
|
|
19408
19408
|
tooltip: "Copy column names",
|
|
19409
19409
|
value: l?.map(([e]) => e).join(",\n") || "",
|
|
@@ -19456,7 +19456,7 @@ function tI({ previewColumn: e, fieldTypes: t, totalRows: n, totalColumns: r, ta
|
|
|
19456
19456
|
externalType: r,
|
|
19457
19457
|
previewColumn: e,
|
|
19458
19458
|
table: a,
|
|
19459
|
-
showOnlyColumnState:
|
|
19459
|
+
showOnlyColumnState: m,
|
|
19460
19460
|
defaultExpanded: o === 0
|
|
19461
19461
|
}, `${i}-${t}`);
|
|
19462
19462
|
})]
|
|
@@ -27066,7 +27066,8 @@ var FV = q.lazy(() => import("./vega-component-ovBXgp91.js")), IV = class {
|
|
|
27066
27066
|
tagName = "marimo-accordion";
|
|
27067
27067
|
validator = B({
|
|
27068
27068
|
labels: U(V()),
|
|
27069
|
-
multiple: W()
|
|
27069
|
+
multiple: W(),
|
|
27070
|
+
expanded: U(V()).default([])
|
|
27070
27071
|
});
|
|
27071
27072
|
render(e) {
|
|
27072
27073
|
return /* @__PURE__ */ (0, J.jsx)(RV, {
|
|
@@ -27075,10 +27076,19 @@ var FV = q.lazy(() => import("./vega-component-ovBXgp91.js")), IV = class {
|
|
|
27075
27076
|
});
|
|
27076
27077
|
}
|
|
27077
27078
|
}, RV = (e) => {
|
|
27078
|
-
let t = (0, Y.c)(
|
|
27079
|
-
|
|
27079
|
+
let t = (0, Y.c)(11), { labels: n, multiple: r, expanded: i, children: a } = e, o;
|
|
27080
|
+
t[0] !== i || t[1] !== r ? (o = r ? {
|
|
27081
|
+
type: "multiple",
|
|
27082
|
+
defaultValue: i
|
|
27083
|
+
} : {
|
|
27084
|
+
type: "single",
|
|
27085
|
+
defaultValue: i[0],
|
|
27086
|
+
collapsible: !0
|
|
27087
|
+
}, t[0] = i, t[1] = r, t[2] = o) : o = t[2];
|
|
27088
|
+
let s = o, c;
|
|
27089
|
+
if (t[3] !== a || t[4] !== n) {
|
|
27080
27090
|
let e;
|
|
27081
|
-
t[
|
|
27091
|
+
t[6] === n ? e = t[7] : (e = (e, t) => /* @__PURE__ */ (0, J.jsxs)(yt, {
|
|
27082
27092
|
value: t.toString(),
|
|
27083
27093
|
children: [/* @__PURE__ */ (0, J.jsx)(wt, {
|
|
27084
27094
|
className: "py-2 text-md",
|
|
@@ -27087,15 +27097,14 @@ var FV = q.lazy(() => import("./vega-component-ovBXgp91.js")), IV = class {
|
|
|
27087
27097
|
className: "text-md",
|
|
27088
27098
|
children: e
|
|
27089
27099
|
})]
|
|
27090
|
-
}, t), t[
|
|
27091
|
-
} else
|
|
27092
|
-
let
|
|
27093
|
-
return t[
|
|
27094
|
-
|
|
27100
|
+
}, t), t[6] = n, t[7] = e), c = q.Children.map(a, e), t[3] = a, t[4] = n, t[5] = c;
|
|
27101
|
+
} else c = t[5];
|
|
27102
|
+
let l;
|
|
27103
|
+
return t[8] !== s || t[9] !== c ? (l = /* @__PURE__ */ (0, J.jsx)(Ze, {
|
|
27104
|
+
...s,
|
|
27095
27105
|
className: "text-muted-foreground",
|
|
27096
|
-
|
|
27097
|
-
|
|
27098
|
-
}), t[5] = o, t[6] = a, t[7] = s) : s = t[7], s;
|
|
27106
|
+
children: c
|
|
27107
|
+
}), t[8] = s, t[9] = c, t[10] = l) : l = t[10], l;
|
|
27099
27108
|
}, zV = {
|
|
27100
27109
|
neutral: "neutral",
|
|
27101
27110
|
info: "info",
|
|
@@ -35311,7 +35320,7 @@ function lJ(e) {
|
|
|
35311
35320
|
}
|
|
35312
35321
|
//#endregion
|
|
35313
35322
|
//#region src/components/editor/renderers/slides-layout/slides-layout.tsx
|
|
35314
|
-
var uJ = q.lazy(() => import("./reveal-component-
|
|
35323
|
+
var uJ = q.lazy(() => import("./reveal-component-BrRhgqQO.js")), dJ = (e) => {
|
|
35315
35324
|
let t = (0, Y.c)(33), { layout: n, setLayout: r, cells: i, mode: a } = e, o = Xm(it), s = a === "read" || o, c = Xm(ke) > 1, [l, u] = (0, q.useState)(null), d;
|
|
35316
35325
|
t[0] !== i || t[1] !== n ? (d = sW(i, n), t[0] = i, t[1] = n, t[2] = d) : d = t[2];
|
|
35317
35326
|
let { slideCells: f, skippedIds: p, noOutputIds: m, slideTypes: h, startCellIndex: g } = d, _;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { O as e, nt as t, rt as n } from "./html-to-image-
|
|
1
|
+
import { O as e, nt as t, rt as n } from "./html-to-image-B3oyXfx7.js";
|
|
2
2
|
import { u as r } from "./dist-3_o3T78I.js";
|
|
3
3
|
import { b as i } from "./select-3qHpFKF9.js";
|
|
4
4
|
import { n as a } from "./semaphore-89jsoHtS.js";
|
|
@@ -2,9 +2,9 @@ import { a as e } from "./rolldown-runtime-Cb76FRpK.js";
|
|
|
2
2
|
import { c as t, g as n, h as r, m as i, t as a } from "./button-CKBkqR9J.js";
|
|
3
3
|
import { t as o } from "./react-DMWcHin-.js";
|
|
4
4
|
import { t as s } from "./compiler-runtime-CAH9RLw2.js";
|
|
5
|
-
import { kt as c, st as l } from "./html-to-image-
|
|
5
|
+
import { kt as c, st as l } from "./html-to-image-B3oyXfx7.js";
|
|
6
6
|
import { u } from "./dist-3_o3T78I.js";
|
|
7
|
-
import { _n as d, c as f, ct as p, g as m, h, l as g, lt as _, n as v, o as y, r as b, t as x, u as S, ut as C, vn as w, xn as T } from "./common-
|
|
7
|
+
import { _n as d, c as f, ct as p, g as m, h, l as g, lt as _, n as v, o as y, r as b, t as x, u as S, ut as C, vn as w, xn as T } from "./common-D5xpkGya.js";
|
|
8
8
|
import { R as E } from "./select-3qHpFKF9.js";
|
|
9
9
|
import { Y as D } from "./dropdown-menu-DhCfcscX.js";
|
|
10
10
|
import { t as O } from "./jsx-runtime-CpoKNRgu.js";
|
package/package.json
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
EyeIcon,
|
|
11
11
|
EyeOffIcon,
|
|
12
12
|
} from "lucide-react";
|
|
13
|
-
import {
|
|
13
|
+
import { useState } from "react";
|
|
14
14
|
import { useLocale } from "react-aria";
|
|
15
15
|
import {
|
|
16
16
|
AddDataframeChart,
|
|
@@ -98,11 +98,7 @@ export function ColumnExplorerPanel<TData>({
|
|
|
98
98
|
hiddenColumns: hiddenColumnCount,
|
|
99
99
|
} = getColumnCountForDisplay(table, totalColumns);
|
|
100
100
|
const { visible: visibleColumnCount } = getUserColumnVisibilityCounts(table);
|
|
101
|
-
const
|
|
102
|
-
const showOnlyColumnState = useMemo(
|
|
103
|
-
() => getShowOnlyColumnState(table),
|
|
104
|
-
[table, columnVisibility],
|
|
105
|
-
);
|
|
101
|
+
const showOnlyColumnState = getShowOnlyColumnState(table);
|
|
106
102
|
|
|
107
103
|
const { rowsAndColumns, hiddenSuffix } = prettifyRowColumnCount({
|
|
108
104
|
numRows: totalRows,
|
|
@@ -6,7 +6,7 @@ const Slot = SlotPrimitive.Slot;
|
|
|
6
6
|
|
|
7
7
|
import { useAtomValue } from "jotai";
|
|
8
8
|
import { ExternalLinkIcon, TriangleAlertIcon } from "lucide-react";
|
|
9
|
-
import React, { type PropsWithChildren,
|
|
9
|
+
import React, { type PropsWithChildren, useState } from "react";
|
|
10
10
|
import { CopyClipboardIcon } from "@/components/icons/copy-icon";
|
|
11
11
|
import { useImperativeModal } from "@/components/modal/ImperativeModal";
|
|
12
12
|
import { Button } from "@/components/ui/button";
|
|
@@ -94,8 +94,7 @@ export const FeedbackModal: React.FC<{
|
|
|
94
94
|
);
|
|
95
95
|
|
|
96
96
|
const notebook = useAtomValue(notebookAtom);
|
|
97
|
-
|
|
98
|
-
const errors = useMemo(() => getCellErrorEntries(store), [notebook]);
|
|
97
|
+
const errors = getCellErrorEntries(store);
|
|
99
98
|
|
|
100
99
|
const cells = notebook.cellIds.inOrderIds.map(
|
|
101
100
|
(cellId) => notebook.cellData[cellId],
|
|
@@ -145,10 +144,9 @@ export const FeedbackModal: React.FC<{
|
|
|
145
144
|
return contents;
|
|
146
145
|
}, [includeCode, notebookSourceAvailable, readCode]);
|
|
147
146
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
}
|
|
147
|
+
let githubIssueUrl = Constants.bugReportUrl;
|
|
148
|
+
let omitted: string[] = [];
|
|
149
|
+
if (environment) {
|
|
152
150
|
const fields: Record<string, string> = {
|
|
153
151
|
env: formatEnvironmentSection(environment),
|
|
154
152
|
};
|
|
@@ -158,8 +156,11 @@ export const FeedbackModal: React.FC<{
|
|
|
158
156
|
if (includeCode && codeRequest.data) {
|
|
159
157
|
fields["reproduction-code"] = formatCodeSection(codeRequest.data);
|
|
160
158
|
}
|
|
161
|
-
|
|
162
|
-
|
|
159
|
+
({ url: githubIssueUrl, omitted } = buildBugReportUrl(
|
|
160
|
+
Constants.bugReportUrl,
|
|
161
|
+
fields,
|
|
162
|
+
));
|
|
163
|
+
}
|
|
163
164
|
|
|
164
165
|
const omittedLabels = omitted
|
|
165
166
|
.map((field) =>
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { render, screen } from "@testing-library/react";
|
|
4
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
import { ReadonlyDiff } from "../readonly-diff";
|
|
6
|
+
|
|
7
|
+
const mocks = vi.hoisted(() => ({
|
|
8
|
+
mergeView: vi.fn(),
|
|
9
|
+
useTheme: vi.fn(),
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
vi.mock("@codemirror/merge", () => ({
|
|
13
|
+
unifiedMergeView: mocks.mergeView,
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
vi.mock("@uiw/react-codemirror", () => ({
|
|
17
|
+
default: ({ theme, value }: { theme: string; value: string }) => (
|
|
18
|
+
<div data-testid="code-mirror" data-theme={theme} data-value={value} />
|
|
19
|
+
),
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
vi.mock("@/theme/useTheme", () => ({
|
|
23
|
+
useTheme: mocks.useTheme,
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
describe("ReadonlyDiff", () => {
|
|
27
|
+
beforeEach(() => {
|
|
28
|
+
mocks.mergeView.mockReset();
|
|
29
|
+
mocks.mergeView.mockReturnValue([]);
|
|
30
|
+
mocks.useTheme.mockReturnValue({ theme: "light" });
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("updates modified code without rebuilding the diff extensions", () => {
|
|
34
|
+
const { rerender } = render(
|
|
35
|
+
<ReadonlyDiff original="before" modified="first revision" />,
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
expect(screen.getByTestId("code-mirror")).toHaveAttribute(
|
|
39
|
+
"data-value",
|
|
40
|
+
"first revision",
|
|
41
|
+
);
|
|
42
|
+
expect(mocks.mergeView).toHaveBeenCalledTimes(1);
|
|
43
|
+
|
|
44
|
+
rerender(<ReadonlyDiff original="before" modified="second revision" />);
|
|
45
|
+
|
|
46
|
+
expect(screen.getByTestId("code-mirror")).toHaveAttribute(
|
|
47
|
+
"data-value",
|
|
48
|
+
"second revision",
|
|
49
|
+
);
|
|
50
|
+
expect(mocks.mergeView).toHaveBeenCalledTimes(1);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("rebuilds diff extensions when the original code changes", () => {
|
|
54
|
+
const { rerender } = render(
|
|
55
|
+
<ReadonlyDiff original="before" modified="after" />,
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
rerender(<ReadonlyDiff original="earlier" modified="after" />);
|
|
59
|
+
|
|
60
|
+
expect(mocks.mergeView).toHaveBeenCalledTimes(2);
|
|
61
|
+
expect(mocks.mergeView).toHaveBeenLastCalledWith(
|
|
62
|
+
expect.objectContaining({ original: "earlier" }),
|
|
63
|
+
);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { act, fireEvent, render, screen } from "@testing-library/react";
|
|
4
|
+
import type React from "react";
|
|
5
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
6
|
+
import { RefreshIconButton } from "../tree-actions";
|
|
7
|
+
|
|
8
|
+
vi.mock("@/components/ui/tooltip", () => ({
|
|
9
|
+
Tooltip: ({ children }: { children: React.ReactNode }) => children,
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
describe("RefreshIconButton", () => {
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
vi.useFakeTimers();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
afterEach(() => {
|
|
18
|
+
vi.useRealTimers();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("spins for at least 500ms after a refresh completes", async () => {
|
|
22
|
+
const onClick = vi.fn().mockResolvedValue(undefined);
|
|
23
|
+
const { container } = render(<RefreshIconButton onClick={onClick} />);
|
|
24
|
+
|
|
25
|
+
fireEvent.click(screen.getByRole("button"));
|
|
26
|
+
|
|
27
|
+
expect(onClick).toHaveBeenCalledOnce();
|
|
28
|
+
expect(container.querySelector("svg")).toHaveClass("animate-[spin_0.5s]");
|
|
29
|
+
|
|
30
|
+
await act(async () => {
|
|
31
|
+
await vi.advanceTimersByTimeAsync(500);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
expect(container.querySelector("svg")).not.toHaveClass(
|
|
35
|
+
"animate-[spin_0.5s]",
|
|
36
|
+
);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("uses the latest refresh callback after a rerender", async () => {
|
|
40
|
+
const initialOnClick = vi.fn().mockResolvedValue(undefined);
|
|
41
|
+
const updatedOnClick = vi.fn().mockResolvedValue(undefined);
|
|
42
|
+
const { rerender } = render(<RefreshIconButton onClick={initialOnClick} />);
|
|
43
|
+
|
|
44
|
+
rerender(<RefreshIconButton onClick={updatedOnClick} />);
|
|
45
|
+
fireEvent.click(screen.getByRole("button"));
|
|
46
|
+
|
|
47
|
+
expect(initialOnClick).not.toHaveBeenCalled();
|
|
48
|
+
expect(updatedOnClick).toHaveBeenCalledOnce();
|
|
49
|
+
|
|
50
|
+
await act(async () => {
|
|
51
|
+
await vi.advanceTimersByTimeAsync(500);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
});
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
MoreVerticalIcon,
|
|
8
8
|
RefreshCwIcon,
|
|
9
9
|
} from "lucide-react";
|
|
10
|
-
import React, {
|
|
10
|
+
import React, { useState } from "react";
|
|
11
11
|
import { Button, type ButtonProps } from "@/components/ui/button";
|
|
12
12
|
import { Tooltip } from "@/components/ui/tooltip";
|
|
13
13
|
import { cn } from "@/utils/cn";
|
|
@@ -41,19 +41,16 @@ export const RefreshIconButton: React.FC<{
|
|
|
41
41
|
}> = ({ onClick, tooltip = "Refresh", className, iconClassName }) => {
|
|
42
42
|
const [isSpinning, setIsSpinning] = useState(false);
|
|
43
43
|
|
|
44
|
-
const handleClick =
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
},
|
|
55
|
-
[onClick],
|
|
56
|
-
);
|
|
44
|
+
const handleClick = async (e: React.MouseEvent) => {
|
|
45
|
+
setIsSpinning(true);
|
|
46
|
+
// Artificially spin for 500ms to show the user that the button is working.
|
|
47
|
+
const minDelay = new Promise<void>((r) => setTimeout(r, 500));
|
|
48
|
+
try {
|
|
49
|
+
await Promise.all([onClick(e), minDelay]);
|
|
50
|
+
} finally {
|
|
51
|
+
setIsSpinning(false);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
57
54
|
|
|
58
55
|
const button = (
|
|
59
56
|
<Button
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
3
|
import { useAtomValue, useStore } from "jotai";
|
|
4
|
-
import { useMemo } from "react";
|
|
5
4
|
import {
|
|
6
5
|
useDeleteCellCallback,
|
|
7
6
|
useDeleteManyCellsCallback,
|
|
@@ -130,21 +129,18 @@ export function usePendingDeleteService() {
|
|
|
130
129
|
const { entries } = useAtomValue(pendingDeleteStateAtom);
|
|
131
130
|
const deleteCell = useDeleteCellCallback();
|
|
132
131
|
const deleteManyCells = useDeleteManyCellsCallback();
|
|
133
|
-
return
|
|
134
|
-
() =>
|
|
135
|
-
submit
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}),
|
|
146
|
-
[store, submit, clear, entries, deleteCell, deleteManyCells],
|
|
147
|
-
);
|
|
132
|
+
return {
|
|
133
|
+
submit: (cellIds: CellId[]) => {
|
|
134
|
+
submit({ cellIds, deleteCell, deleteManyCells, store });
|
|
135
|
+
},
|
|
136
|
+
clear,
|
|
137
|
+
get idle() {
|
|
138
|
+
return entries.size === 0;
|
|
139
|
+
},
|
|
140
|
+
get shouldConfirmDelete() {
|
|
141
|
+
return entries.size > 1;
|
|
142
|
+
},
|
|
143
|
+
};
|
|
148
144
|
}
|
|
149
145
|
|
|
150
146
|
export function usePendingDelete(cellId: CellId) {
|
|
@@ -17,6 +17,7 @@ import type {
|
|
|
17
17
|
import type { DataSourceConnection } from "@/core/datasets/data-source-connections";
|
|
18
18
|
import {
|
|
19
19
|
dataSourceConnectionsAtom,
|
|
20
|
+
exportedForTesting as dataSourceTesting,
|
|
20
21
|
setLatestEngineSelected,
|
|
21
22
|
} from "@/core/datasets/data-source-connections";
|
|
22
23
|
import { type ConnectionName, DUCKDB_ENGINE } from "@/core/datasets/engines";
|
|
@@ -550,8 +551,8 @@ _df = mo.sql(
|
|
|
550
551
|
const getLatestEngine = () =>
|
|
551
552
|
store.get(dataSourceConnectionsAtom).latestEngineSelected;
|
|
552
553
|
|
|
553
|
-
it("should
|
|
554
|
-
expect(getLatestEngine()).
|
|
554
|
+
it("should have no selected engine initially", () => {
|
|
555
|
+
expect(getLatestEngine()).toBeNull();
|
|
555
556
|
});
|
|
556
557
|
|
|
557
558
|
it("should persist the selected engine", () => {
|
|
@@ -622,6 +623,73 @@ _df = mo.sql(
|
|
|
622
623
|
});
|
|
623
624
|
|
|
624
625
|
describe("defaultCode", () => {
|
|
626
|
+
it("should prefer the newest usable connection until an engine is selected", () => {
|
|
627
|
+
const previousState = store.get(dataSourceConnectionsAtom);
|
|
628
|
+
const connection: DataSourceConnection = {
|
|
629
|
+
name: "first_connection" as ConnectionName,
|
|
630
|
+
source: "sqlite",
|
|
631
|
+
display_name: "SQLite",
|
|
632
|
+
dialect: "sqlite",
|
|
633
|
+
databases: [],
|
|
634
|
+
};
|
|
635
|
+
const newestConnection = {
|
|
636
|
+
...connection,
|
|
637
|
+
name: "newest_connection" as ConnectionName,
|
|
638
|
+
};
|
|
639
|
+
const icebergConnection = {
|
|
640
|
+
...connection,
|
|
641
|
+
name: "catalog" as ConnectionName,
|
|
642
|
+
source: "iceberg",
|
|
643
|
+
};
|
|
644
|
+
try {
|
|
645
|
+
store.set(dataSourceConnectionsAtom, {
|
|
646
|
+
latestEngineSelected: null,
|
|
647
|
+
connectionsMap: new Map(),
|
|
648
|
+
});
|
|
649
|
+
expect(adapter.defaultMetadata.engine).toBe(DUCKDB_ENGINE);
|
|
650
|
+
expect(adapter.defaultCode).toBe('_df = mo.sql(f"""SELECT * FROM """)');
|
|
651
|
+
|
|
652
|
+
store.set(dataSourceConnectionsAtom, {
|
|
653
|
+
latestEngineSelected: null,
|
|
654
|
+
connectionsMap: new Map([
|
|
655
|
+
[connection.name, connection],
|
|
656
|
+
[newestConnection.name, newestConnection],
|
|
657
|
+
[icebergConnection.name, icebergConnection],
|
|
658
|
+
[DUCKDB_ENGINE, { ...connection, name: DUCKDB_ENGINE }],
|
|
659
|
+
]),
|
|
660
|
+
});
|
|
661
|
+
expect(adapter.defaultMetadata.engine).toBe(newestConnection.name);
|
|
662
|
+
expect(adapter.defaultCode).toContain("engine=newest_connection");
|
|
663
|
+
expect(
|
|
664
|
+
store.get(dataSourceConnectionsAtom).latestEngineSelected,
|
|
665
|
+
).toBeNull();
|
|
666
|
+
|
|
667
|
+
const refreshConnection = (connection: DataSourceConnection) => {
|
|
668
|
+
store.set(
|
|
669
|
+
dataSourceConnectionsAtom,
|
|
670
|
+
dataSourceTesting.reducer(store.get(dataSourceConnectionsAtom), {
|
|
671
|
+
type: "addDataSourceConnection",
|
|
672
|
+
payload: { connections: [connection] },
|
|
673
|
+
}),
|
|
674
|
+
);
|
|
675
|
+
};
|
|
676
|
+
refreshConnection(connection);
|
|
677
|
+
expect(adapter.defaultMetadata.engine).toBe(connection.name);
|
|
678
|
+
expect(adapter.defaultCode).toContain("engine=first_connection");
|
|
679
|
+
|
|
680
|
+
setLatestEngineSelected(connection.name);
|
|
681
|
+
refreshConnection(newestConnection);
|
|
682
|
+
expect(adapter.defaultCode).toContain("engine=first_connection");
|
|
683
|
+
|
|
684
|
+
setLatestEngineSelected(DUCKDB_ENGINE);
|
|
685
|
+
refreshConnection(connection);
|
|
686
|
+
expect(adapter.defaultMetadata.engine).toBe(DUCKDB_ENGINE);
|
|
687
|
+
expect(adapter.defaultCode).toBe('_df = mo.sql(f"""SELECT * FROM """)');
|
|
688
|
+
} finally {
|
|
689
|
+
store.set(dataSourceConnectionsAtom, previousState);
|
|
690
|
+
}
|
|
691
|
+
});
|
|
692
|
+
|
|
625
693
|
it("should include engine in defaultCode when selected", () => {
|
|
626
694
|
const engine = "postgres_engine" as ConnectionName;
|
|
627
695
|
setLatestEngineSelected(engine);
|
|
@@ -76,7 +76,22 @@ export interface SQLLanguageAdapterMetadata extends SQLMetadata {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
function getLatestEngine(): ConnectionName {
|
|
79
|
-
|
|
79
|
+
const { latestEngineSelected, connectionsMap } = store.get(
|
|
80
|
+
dataSourceConnectionsAtom,
|
|
81
|
+
);
|
|
82
|
+
if (latestEngineSelected !== null) {
|
|
83
|
+
return latestEngineSelected;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Map insertion order prefers the most recently discovered connection.
|
|
87
|
+
const connections = [...connectionsMap.values()].reverse();
|
|
88
|
+
return (
|
|
89
|
+
connections.find(
|
|
90
|
+
(connection) =>
|
|
91
|
+
!INTERNAL_SQL_ENGINES.has(connection.name) &&
|
|
92
|
+
connection.source !== "iceberg",
|
|
93
|
+
)?.name ?? DUCKDB_ENGINE
|
|
94
|
+
);
|
|
80
95
|
}
|
|
81
96
|
|
|
82
97
|
/**
|
|
@@ -106,6 +106,7 @@ describe("data source connections", () => {
|
|
|
106
106
|
|
|
107
107
|
let newState = addConnection(connections, state);
|
|
108
108
|
expect(newState.connectionsMap.size).toBe(defaultConnSize + 2);
|
|
109
|
+
newState = { ...newState, latestEngineSelected: connections[0].name };
|
|
109
110
|
|
|
110
111
|
newState = reducer(newState, {
|
|
111
112
|
type: "removeDataSourceConnection",
|
|
@@ -113,6 +114,7 @@ describe("data source connections", () => {
|
|
|
113
114
|
});
|
|
114
115
|
expect(newState.connectionsMap.size).toBe(defaultConnSize + 1);
|
|
115
116
|
expect(newState.connectionsMap.has("conn2" as ConnectionName)).toBe(true);
|
|
117
|
+
expect(newState.latestEngineSelected).toBeNull();
|
|
116
118
|
});
|
|
117
119
|
|
|
118
120
|
it("can clear all connections", () => {
|
|
@@ -44,7 +44,8 @@ export interface DataSourceConnection extends Omit<
|
|
|
44
44
|
export type ConnectionsMap = ReadonlyMap<ConnectionName, DataSourceConnection>;
|
|
45
45
|
|
|
46
46
|
export interface DataSourceState {
|
|
47
|
-
|
|
47
|
+
// Null permits automatic selection; DuckDB can also be an explicit choice.
|
|
48
|
+
latestEngineSelected: ConnectionName | null;
|
|
48
49
|
connectionsMap: ConnectionsMap;
|
|
49
50
|
}
|
|
50
51
|
|
|
@@ -110,7 +111,7 @@ function tableSchemaPath(sqlTableContext: SQLTableContext): string[] {
|
|
|
110
111
|
|
|
111
112
|
function initialState(): DataSourceState {
|
|
112
113
|
return {
|
|
113
|
-
latestEngineSelected:
|
|
114
|
+
latestEngineSelected: null,
|
|
114
115
|
connectionsMap: initialConnections,
|
|
115
116
|
};
|
|
116
117
|
}
|
|
@@ -136,6 +137,8 @@ const {
|
|
|
136
137
|
// Backend will dedupe by connection name & keep the latest, so we use this as the key
|
|
137
138
|
const newMap = new Map(connectionsMap);
|
|
138
139
|
for (const conn of opts.connections) {
|
|
140
|
+
// Refresh insertion order so automatic selection follows discovery recency.
|
|
141
|
+
newMap.delete(conn.name);
|
|
139
142
|
newMap.set(conn.name, conn);
|
|
140
143
|
}
|
|
141
144
|
|
|
@@ -161,16 +164,17 @@ const {
|
|
|
161
164
|
}),
|
|
162
165
|
);
|
|
163
166
|
return {
|
|
164
|
-
//
|
|
165
|
-
latestEngineSelected:
|
|
166
|
-
|
|
167
|
-
|
|
167
|
+
// Allow automatic selection again when the selected connection disappears.
|
|
168
|
+
latestEngineSelected:
|
|
169
|
+
latestEngineSelected && newMap.has(latestEngineSelected)
|
|
170
|
+
? latestEngineSelected
|
|
171
|
+
: null,
|
|
168
172
|
connectionsMap: newMap,
|
|
169
173
|
};
|
|
170
174
|
},
|
|
171
175
|
|
|
172
176
|
clearDataSourceConnections: (): DataSourceState => ({
|
|
173
|
-
latestEngineSelected:
|
|
177
|
+
latestEngineSelected: null,
|
|
174
178
|
connectionsMap: new Map(),
|
|
175
179
|
}),
|
|
176
180
|
|
|
@@ -183,9 +187,10 @@ const {
|
|
|
183
187
|
const newMap = new Map(connectionsMap);
|
|
184
188
|
newMap.delete(connectionName);
|
|
185
189
|
return {
|
|
186
|
-
latestEngineSelected:
|
|
187
|
-
|
|
188
|
-
|
|
190
|
+
latestEngineSelected:
|
|
191
|
+
latestEngineSelected && newMap.has(latestEngineSelected)
|
|
192
|
+
? latestEngineSelected
|
|
193
|
+
: null,
|
|
189
194
|
connectionsMap: newMap,
|
|
190
195
|
};
|
|
191
196
|
},
|
|
@@ -23,6 +23,7 @@ interface Data {
|
|
|
23
23
|
* Whether to allow multiple tabs to be open.
|
|
24
24
|
*/
|
|
25
25
|
multiple: boolean;
|
|
26
|
+
expanded: string[];
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
export class AccordionPlugin implements IStatelessPlugin<Data> {
|
|
@@ -31,6 +32,7 @@ export class AccordionPlugin implements IStatelessPlugin<Data> {
|
|
|
31
32
|
validator = z.object({
|
|
32
33
|
labels: z.array(z.string()),
|
|
33
34
|
multiple: z.boolean(),
|
|
35
|
+
expanded: z.array(z.string()).default([]),
|
|
34
36
|
});
|
|
35
37
|
|
|
36
38
|
render(props: IStatelessPluginProps<Data>): JSX.Element {
|
|
@@ -43,11 +45,14 @@ export class AccordionPlugin implements IStatelessPlugin<Data> {
|
|
|
43
45
|
const AccordionComponent = ({
|
|
44
46
|
labels,
|
|
45
47
|
multiple,
|
|
48
|
+
expanded,
|
|
46
49
|
children,
|
|
47
50
|
}: PropsWithChildren<Data>): JSX.Element => {
|
|
48
|
-
const
|
|
51
|
+
const expansionProps = multiple
|
|
52
|
+
? { type: "multiple" as const, defaultValue: expanded }
|
|
53
|
+
: { type: "single" as const, defaultValue: expanded[0], collapsible: true };
|
|
49
54
|
return (
|
|
50
|
-
<Accordion
|
|
55
|
+
<Accordion {...expansionProps} className="text-muted-foreground">
|
|
51
56
|
{React.Children.map(children, (child, index) => {
|
|
52
57
|
return (
|
|
53
58
|
<AccordionItem key={index} value={index.toString()}>
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
import { cleanup, fireEvent, render } from "@testing-library/react";
|
|
3
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
4
|
+
import { AccordionPlugin } from "../AccordionPlugin";
|
|
5
|
+
|
|
6
|
+
const plugin = new AccordionPlugin();
|
|
7
|
+
|
|
8
|
+
function renderAccordion(multiple: boolean, expanded?: string[]) {
|
|
9
|
+
return plugin.render({
|
|
10
|
+
host: document.createElement("marimo-accordion"),
|
|
11
|
+
data: plugin.validator.parse({
|
|
12
|
+
labels: ["Summary", "Details"],
|
|
13
|
+
multiple,
|
|
14
|
+
expanded,
|
|
15
|
+
}),
|
|
16
|
+
children: [
|
|
17
|
+
<p key="summary">Overview</p>,
|
|
18
|
+
<p key="details">More information</p>,
|
|
19
|
+
],
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe("AccordionPlugin", () => {
|
|
24
|
+
afterEach(cleanup);
|
|
25
|
+
|
|
26
|
+
it.each([
|
|
27
|
+
{ multiple: false, expanded: undefined, expected: ["false", "false"] },
|
|
28
|
+
{ multiple: true, expanded: [], expected: ["false", "false"] },
|
|
29
|
+
{ multiple: false, expanded: ["0"], expected: ["true", "false"] },
|
|
30
|
+
{ multiple: false, expanded: ["1"], expected: ["false", "true"] },
|
|
31
|
+
{ multiple: true, expanded: ["0", "1"], expected: ["true", "true"] },
|
|
32
|
+
])(
|
|
33
|
+
"sets initial expansion: $multiple, $expanded",
|
|
34
|
+
({ multiple, expanded, expected }) => {
|
|
35
|
+
const { getAllByRole } = render(renderAccordion(multiple, expanded));
|
|
36
|
+
expect(
|
|
37
|
+
getAllByRole("button").map((button) =>
|
|
38
|
+
button.getAttribute("aria-expanded"),
|
|
39
|
+
),
|
|
40
|
+
).toEqual(expected);
|
|
41
|
+
},
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
it("lets users collapse and switch the initially expanded item", () => {
|
|
45
|
+
const { getAllByRole, rerender } = render(renderAccordion(false, ["1"]));
|
|
46
|
+
const [summary, details] = getAllByRole("button");
|
|
47
|
+
fireEvent.click(details);
|
|
48
|
+
expect(details.getAttribute("aria-expanded")).toBe("false");
|
|
49
|
+
rerender(renderAccordion(false, ["1"]));
|
|
50
|
+
expect(details.getAttribute("aria-expanded")).toBe("false");
|
|
51
|
+
fireEvent.click(details);
|
|
52
|
+
fireEvent.click(summary);
|
|
53
|
+
expect(
|
|
54
|
+
[summary, details].map((button) => button.getAttribute("aria-expanded")),
|
|
55
|
+
).toEqual(["true", "false"]);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("lets users collapse items independently with multiple enabled", () => {
|
|
59
|
+
const { getAllByRole } = render(renderAccordion(true, ["0", "1"]));
|
|
60
|
+
const [summary, details] = getAllByRole("button");
|
|
61
|
+
fireEvent.click(summary);
|
|
62
|
+
expect(
|
|
63
|
+
[summary, details].map((button) => button.getAttribute("aria-expanded")),
|
|
64
|
+
).toEqual(["false", "true"]);
|
|
65
|
+
});
|
|
66
|
+
});
|