@marimo-team/islands 0.25.1-dev2 → 0.25.1-dev20
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-KVhjDMSe.js → ConnectedDataExplorerComponent-CwMXfDQR.js} +3 -3
- package/dist/{ErrorBoundary-DO0ie20N.js → ErrorBoundary-BU3l6Hi2.js} +1 -1
- package/dist/{abnfDiagram-VCTEODGH-CpN0FRda.js → abnfDiagram-VCTEODGH-AFhOQTt8.js} +3 -3
- package/dist/{architectureDiagram-5GKGNRK7-DDyj5SRB.js → architectureDiagram-5GKGNRK7-BngksAAQ.js} +5 -5
- package/dist/{blockDiagram-I7D4REHJ-BbXEXJRu.js → blockDiagram-I7D4REHJ-CvBmppdy.js} +9 -9
- package/dist/{c4Diagram-7LVT6UL2-DcLsbQmR.js → c4Diagram-7LVT6UL2-EceL4_jH.js} +7 -7
- package/dist/{cells-DYIkSxs6.js → cells-Cksce8br.js} +4123 -4150
- package/dist/{channel-B5XnTarr.js → channel-z7HZXHwv.js} +1 -1
- package/dist/{chat-ui-BXj3uYjy.js → chat-ui-ruucqBTA.js} +45 -45
- package/dist/{chunk-4HAMMTFA-D2qv8E0X.js → chunk-4HAMMTFA-BPTTOOC5.js} +5 -5
- package/dist/{chunk-75Z2AOVW-5_HsvXwr.js → chunk-75Z2AOVW-C4AOleKf.js} +1 -1
- package/dist/{chunk-DU6HZSFF-BUgAHvnM.js → chunk-DU6HZSFF-1TwWR6gh.js} +1 -1
- package/dist/{chunk-F27PBJKO-DHmYWZXr.js → chunk-F27PBJKO-DMI1PBe4.js} +1 -1
- package/dist/{chunk-GMAD6QVW-Czynz8aA.js → chunk-GMAD6QVW-vStrMz2P.js} +3 -3
- package/dist/{chunk-GVQU2GXP-yQEdulRS.js → chunk-GVQU2GXP-2HwWgs4u.js} +2 -2
- package/dist/{chunk-IMKFNOWR-BfwTebD0.js → chunk-IMKFNOWR-UOVZuZVn.js} +5 -5
- package/dist/{chunk-L3NEJ4N5-DwRWnDDK.js → chunk-L3NEJ4N5-RhC7i0I2.js} +5 -5
- package/dist/{chunk-OSK3NFVY-BBKU_Ign.js → chunk-OSK3NFVY-CZV09N-U.js} +6 -6
- package/dist/{chunk-P2QGCYS3-C0iGU29N.js → chunk-P2QGCYS3-0TAWai9C.js} +1 -1
- package/dist/{chunk-POPQ4Y6H-BCbS3Ozc.js → chunk-POPQ4Y6H-Cc3_56GX.js} +1 -1
- package/dist/{chunk-PWAF6VOD-CTFCL0jI.js → chunk-PWAF6VOD-lNxB9k8z.js} +1 -1
- package/dist/{chunk-SHT3W25Y-BspJEJ5H.js → chunk-SHT3W25Y-CrEPyWy1.js} +7 -7
- package/dist/{chunk-SVP7TREG-DX3rFNTe.js → chunk-SVP7TREG-uIyyySp6.js} +2 -2
- package/dist/{chunk-TICWLB2K-fVF7Uujg.js → chunk-TICWLB2K-DdXM3eg6.js} +5 -5
- package/dist/classDiagram-ZZMXUADV-BiATbBE4.js +30 -0
- package/dist/classDiagram-v2-VYDZK3BY-JnH97GYw.js +30 -0
- package/dist/{code-visibility-64_ACQvC.js → code-visibility-DDDujOI0.js} +17 -17
- package/dist/{common-LlPE7GR6.js → common-BkYqr2IQ.js} +620 -620
- package/dist/{cynefinDiagram-5FMLGOSQ-CFQrqzSX.js → cynefinDiagram-5FMLGOSQ-zknaAsGy.js} +3 -3
- package/dist/{dagre-GXQ25YYZ-xHNAUTyG.js → dagre-GXQ25YYZ-BhMOoMGJ.js} +10 -10
- package/dist/{data-grid-overlay-editor-BUvmnptP.js → data-grid-overlay-editor-3APq_6Z0.js} +1 -1
- package/dist/{diagram-S7CK7UJ4-Bt1rPbAx.js → diagram-S7CK7UJ4-V5-Z-EDY.js} +4 -4
- package/dist/{diagram-UQ7AKVKN-D1sq5FoZ.js → diagram-UQ7AKVKN-qw8AjDal.js} +3 -3
- package/dist/{diagram-VSXAHHWV-BKpmG23x.js → diagram-VSXAHHWV-CR7BJygZ.js} +2 -2
- package/dist/{diagram-VX7I27RA-BMwGFVD6.js → diagram-VX7I27RA-DfuqFb1E.js} +5 -5
- package/dist/{diagram-Z3DM3KII--KLDR9Pl.js → diagram-Z3DM3KII-DpWwlYZB.js} +3 -3
- package/dist/{dist-BaHjhkpZ.js → dist-CcyKTBTF.js} +1 -1
- package/dist/{ebnfDiagram-PWID7BFC-WnG4cvSQ.js → ebnfDiagram-PWID7BFC-C20OnNMZ.js} +3 -3
- package/dist/{erDiagram-RLTQ6QDP-DhzKiYHc.js → erDiagram-RLTQ6QDP-DBXLWO9v.js} +12 -12
- package/dist/{error-entries-C0NsUqXl.js → error-entries-CvCFdpY2.js} +18 -18
- package/dist/{feedback-button-B98mEc3M.js → feedback-button-DWSXRUAw.js} +19 -19
- package/dist/flowDiagram-HODETNUW-DL52nMkt.js +16 -0
- package/dist/{formats-BR5iGb4s.js → formats-Cl_67ghI.js} +2 -2
- package/dist/{ganttDiagram-EL5Y4UJY-DbzEBLbl.js → ganttDiagram-EL5Y4UJY-cRRaDt88.js} +2 -2
- package/dist/{gitGraphDiagram-WWUBYQGX-BMf0iKi1.js → gitGraphDiagram-WWUBYQGX-Pox5k9-Y.js} +2 -2
- package/dist/{glide-data-editor-CBIPbkXG.js → glide-data-editor-DGmuodBB.js} +3 -3
- package/dist/{globals-VpAtGRbg.js → globals-ciicQhoq.js} +1 -1
- package/dist/{html-to-image-CeYYvQNb.js → html-to-image-CcfkLMrx.js} +32 -31
- package/dist/{infoDiagram-27XIBGKW-BagtCX_5.js → infoDiagram-27XIBGKW-ntpxMfrq.js} +2 -2
- package/dist/{ishikawaDiagram-5VMMS53U-DByDehYy.js → ishikawaDiagram-5VMMS53U-BllIlHE5.js} +3 -3
- package/dist/{journeyDiagram-3NMN7TZE-D2LcKDUO.js → journeyDiagram-3NMN7TZE-EPJDPlt0.js} +2 -2
- package/dist/{kanban-definition-UXKFOSKX-BxwkbBI_.js → kanban-definition-UXKFOSKX-GYtNMjtS.js} +9 -9
- package/dist/{katex-C6D4cX_N.js → katex-BNCmKUol.js} +983 -1120
- package/dist/{loader-D2W4oM-3.js → loader-DEZSzvma.js} +253 -248
- package/dist/main.js +622 -620
- package/dist/{mermaid-DPNuhemn.js → mermaid-Dnzoxjt1.js} +49 -49
- package/dist/{mhchem-DWCF06w9.js → mhchem-BEc7A1or.js} +1 -1
- package/dist/{mindmap-definition-YA3MSWOX-Cgvojw3y.js → mindmap-definition-YA3MSWOX-Cth76xgH.js} +11 -11
- package/dist/{number-overlay-editor-ClZmv3Qw.js → number-overlay-editor-CcdYLWWR.js} +1 -1
- package/dist/{pegDiagram-XKGWAZYB-DahHhYyN.js → pegDiagram-XKGWAZYB-Cu4uOXii.js} +3 -3
- package/dist/{pieDiagram-E7YTZNPT-CCtX0oCK.js → pieDiagram-E7YTZNPT-Bh3eDa9s.js} +3 -3
- package/dist/{process-output-BCTjlyuH.js → process-output-C6apL85F.js} +2 -2
- package/dist/{quadrantDiagram-AXDQQJYC-DftINo6g.js → quadrantDiagram-AXDQQJYC--kqzFC3w.js} +1 -1
- package/dist/{railroadDiagram-O6MQD6OU-Du5NsPUb.js → railroadDiagram-O6MQD6OU-DG-M6Rnc.js} +3 -3
- package/dist/{react-vega-5AcMW8gG.js → react-vega-ewj7q_U5.js} +1 -1
- package/dist/{requirementDiagram-BXWQKSXE-0pmmxL6_.js → requirementDiagram-BXWQKSXE-DCwsThh0.js} +11 -11
- package/dist/{reveal-component-C1s_K6O8.js → reveal-component-DUPORxps.js} +744 -743
- package/dist/{sankeyDiagram-P5KCCOFB-CDKR5TJH.js → sankeyDiagram-P5KCCOFB-BdzhT0BM.js} +1 -1
- package/dist/{sequenceDiagram-WJ2MYXX4-DB-ChWwU.js → sequenceDiagram-WJ2MYXX4-CbXB1_PU.js} +4 -4
- package/dist/{stateDiagram-D77RDMKH-orN6AwMx.js → stateDiagram-D77RDMKH-A-Zf3Sb2.js} +13 -13
- package/dist/stateDiagram-v2-MP3YSRHH-D9YMnUGx.js +30 -0
- package/dist/style.css +1 -1
- package/dist/{swimlanes-42K2YHIH-BkSDcqDT.js → swimlanes-42K2YHIH-B_pMf0J4.js} +10 -10
- package/dist/swimlanesDiagram-VR7AAH4N-Ctgy-yGP.js +30 -0
- package/dist/{timeline-definition-24CTP7MA-BuqJUIRZ.js → timeline-definition-24CTP7MA-DTXFjTTM.js} +3 -3
- package/dist/{toDate-WXg1yLcI.js → toDate-C67bxMWn.js} +139 -111
- package/dist/{vega-component-D0QXopU8.js → vega-component-tFOzgarw.js} +12 -12
- package/dist/{vega-embed-container-CbnTccZi.js → vega-embed-container-sSR5hAsy.js} +2 -2
- package/dist/{vega-loader.browser-D95WfUcj.js → vega-loader.browser-DNST6uVf.js} +2 -2
- package/dist/{vennDiagram-4TSXK5OY-CTUlJ2PK.js → vennDiagram-4TSXK5OY-BPHjZo0R.js} +3 -3
- package/dist/{wardleyDiagram-VM6X3IG4-3evLUb0M.js → wardleyDiagram-VM6X3IG4-6fTLYBuD.js} +3 -3
- package/dist/{xychartDiagram-S5SC5T6Z-DL2E3K8-.js → xychartDiagram-S5SC5T6Z-BYjwjrzE.js} +5 -5
- package/package.json +1 -2
- package/src/components/data-table/charts/__tests__/chart-panel.test.tsx +286 -0
- package/src/components/data-table/charts/charts.tsx +26 -13
- package/src/components/editor/chrome/components/contribute-snippet-button.tsx +6 -1
- package/src/components/editor/notebook-cell.tsx +2 -2
- package/src/components/editor/output/MarimoTracebackOutput.tsx +1 -1
- package/src/components/editor/output/VideoOutput.tsx +1 -1
- package/src/components/editor/output/ansi-reduce.ts +8 -4
- package/src/components/editor/output/console/text-rendering.tsx +3 -1
- package/src/components/home/components.tsx +1 -0
- package/src/components/pages/run-page.tsx +1 -0
- package/src/components/static-html/share-modal.tsx +2 -1
- package/src/components/static-html/static-banner.tsx +2 -2
- package/src/components/ui/links.tsx +6 -1
- package/src/components/ui/typography.tsx +16 -8
- package/src/core/kernel/__tests__/session.test.ts +110 -1
- package/src/core/kernel/session.ts +46 -15
- package/src/css/globals.css +18 -21
- package/src/css/katex.min.css +260 -241
- package/src/plugins/impl/vega/__tests__/utils.test.ts +16 -1
- package/src/plugins/impl/vega/__tests__/vega.test.ts +37 -0
- package/src/plugins/impl/vega/loader.ts +23 -1
- package/src/plugins/impl/vega/utils.ts +5 -4
- package/src/plugins/layout/RoutesPlugin.tsx +4 -6
- package/dist/classDiagram-ZZMXUADV-Fe-V14Sc.js +0 -30
- package/dist/classDiagram-v2-VYDZK3BY-xTDg7lOs.js +0 -30
- package/dist/flowDiagram-HODETNUW-6dMdB8pR.js +0 -16
- package/dist/katex-DFBiSucW.js +0 -21840
- package/dist/stateDiagram-v2-MP3YSRHH-o05_rJN8.js +0 -30
- package/dist/swimlanesDiagram-VR7AAH4N-D_nFQfRc.js +0 -30
|
@@ -237,8 +237,8 @@ function Ye(e) {
|
|
|
237
237
|
if ("spec" in e) return Ye(e.spec);
|
|
238
238
|
}
|
|
239
239
|
}
|
|
240
|
-
function Xe(t) {
|
|
241
|
-
return !t || Object.keys(t).length === 0 ? "auto" : e.mapValues(t, (e) => e === "geometry"
|
|
240
|
+
function Xe(t, { parseDates: n = !1 } = {}) {
|
|
241
|
+
return !t || Object.keys(t).length === 0 ? "auto" : e.mapValues(t, (e) => e === "geometry" ? "string" : e === "date" ? n ? "date" : "string" : e === "time" ? "string" : e === "datetime" ? "date" : e);
|
|
242
242
|
}
|
|
243
243
|
//#endregion
|
|
244
244
|
//#region ../node_modules/.pnpm/d3-dsv@3.0.1/node_modules/d3-dsv/src/dsv.js
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { i as e } from "./chunk-75Z2AOVW-
|
|
1
|
+
import { i as e } from "./chunk-75Z2AOVW-C4AOleKf.js";
|
|
2
2
|
import { t } from "./src-CA_m4ONl.js";
|
|
3
3
|
import { n } from "./chunk-Y2CYZVJY-DKKS1VyV.js";
|
|
4
|
-
import { $ as r, H as i, K as a, U as o, a as s, b as c, c as l, et as u, f as d, nt as f, tt as p, v as m, w as h, y as g } from "./chunk-DU6HZSFF-
|
|
4
|
+
import { $ as r, H as i, K as a, U as o, a as s, b as c, c as l, et as u, f as d, nt as f, tt as p, v as m, w as h, y as g } from "./chunk-DU6HZSFF-1TwWR6gh.js";
|
|
5
5
|
import { t as _ } from "./rough.esm-CtV9AnV1.js";
|
|
6
|
-
import { p as v } from "./mermaid-
|
|
6
|
+
import { p as v } from "./mermaid-Dnzoxjt1.js";
|
|
7
7
|
//#region ../node_modules/.pnpm/khroma@2.1.0/node_modules/khroma/dist/methods/transparentize.js
|
|
8
8
|
var y = (e, t) => p(e, "a", -t), b = 1e-10;
|
|
9
9
|
function x(e, t) {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { i as e } from "./chunk-75Z2AOVW-
|
|
1
|
+
import { i as e } from "./chunk-75Z2AOVW-C4AOleKf.js";
|
|
2
2
|
import { n as t } from "./src-CA_m4ONl.js";
|
|
3
3
|
import { n } from "./chunk-Y2CYZVJY-DKKS1VyV.js";
|
|
4
|
-
import { D as r, H as i, K as a, U as o, a as s, b as c, c as l, v as u, w as d, x as f, y as p } from "./chunk-DU6HZSFF-
|
|
5
|
-
import { p as m } from "./mermaid-
|
|
4
|
+
import { D as r, H as i, K as a, U as o, a as s, b as c, c as l, v as u, w as d, x as f, y as p } from "./chunk-DU6HZSFF-1TwWR6gh.js";
|
|
5
|
+
import { p as m } from "./mermaid-Dnzoxjt1.js";
|
|
6
6
|
import { n as h } from "./mermaid-parser.core-DxuSfYwJ.js";
|
|
7
7
|
import { t as g } from "./chunk-JWPE2WC7-DJvv1t8U.js";
|
|
8
8
|
//#region ../node_modules/.pnpm/mermaid@11.17.2/node_modules/mermaid/dist/chunks/mermaid.core/wardleyDiagram-VM6X3IG4.mjs
|
|
@@ -2,14 +2,14 @@ import { t as e } from "./linear-Cnaq3Tfb.js";
|
|
|
2
2
|
import { n as t } from "./ordinal-c7efuCSS.js";
|
|
3
3
|
import { t as n } from "./range-BAIvbVHp.js";
|
|
4
4
|
import { q as r } from "./cubic-OaEBhQPK.js";
|
|
5
|
-
import { i } from "./chunk-75Z2AOVW-
|
|
5
|
+
import { i } from "./chunk-75Z2AOVW-C4AOleKf.js";
|
|
6
6
|
import { n as a } from "./init-Ix4LXMlt.js";
|
|
7
7
|
import { n as o } from "./src-CA_m4ONl.js";
|
|
8
8
|
import { n as s } from "./chunk-Y2CYZVJY-DKKS1VyV.js";
|
|
9
|
-
import { D as c, H as l, K as u, U as d, a as f, b as p, c as m, f as h, v as g, w as _, y as v, z as y } from "./chunk-DU6HZSFF-
|
|
10
|
-
import "./chunk-PWAF6VOD-
|
|
11
|
-
import { t as b } from "./chunk-GMAD6QVW-
|
|
12
|
-
import { p as x } from "./mermaid-
|
|
9
|
+
import { D as c, H as l, K as u, U as d, a as f, b as p, c as m, f as h, v as g, w as _, y as v, z as y } from "./chunk-DU6HZSFF-1TwWR6gh.js";
|
|
10
|
+
import "./chunk-PWAF6VOD-lNxB9k8z.js";
|
|
11
|
+
import { t as b } from "./chunk-GMAD6QVW-vStrMz2P.js";
|
|
12
|
+
import { p as x } from "./mermaid-Dnzoxjt1.js";
|
|
13
13
|
//#region ../node_modules/.pnpm/d3-scale@4.0.2/node_modules/d3-scale/src/band.js
|
|
14
14
|
function S() {
|
|
15
15
|
var e = t().unknown(void 0), r = e.domain, i = e.range, o = 0, s = 1, c, l, u = !1, d = 0, f = 0, p = .5;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@marimo-team/islands",
|
|
3
|
-
"version": "0.25.1-
|
|
3
|
+
"version": "0.25.1-dev20",
|
|
4
4
|
"main": "dist/main.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -198,7 +198,6 @@
|
|
|
198
198
|
"@testing-library/jest-dom": "^7.0.1",
|
|
199
199
|
"@testing-library/react": "^16.3.2",
|
|
200
200
|
"@types/dompurify": "^3.2.0",
|
|
201
|
-
"@types/katex": "^0.16.8",
|
|
202
201
|
"@types/lodash-es": "^4.17.12",
|
|
203
202
|
"@types/node": "^24.13.3",
|
|
204
203
|
"@types/plotly.js": "^2.35.16",
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { fireEvent, render, waitFor } from "@testing-library/react";
|
|
4
|
+
import { createStore, Provider } from "jotai";
|
|
5
|
+
import { Tooltip } from "radix-ui";
|
|
6
|
+
import type { ComponentProps } from "react";
|
|
7
|
+
import { beforeAll, describe, expect, it, vi } from "vitest";
|
|
8
|
+
import { SetupMocks } from "@/__mocks__/common";
|
|
9
|
+
import { cellId } from "@/__tests__/branded";
|
|
10
|
+
import { LazyVegaEmbed } from "@/components/charts/lazy";
|
|
11
|
+
import { vegaLoader } from "@/plugins/impl/vega/loader";
|
|
12
|
+
import { ChartPanel, TablePanel, type TablePanelProps } from "../charts";
|
|
13
|
+
import { getChartTabName, tabsStorageAtom } from "../storage";
|
|
14
|
+
import { ChartType, NONE_VALUE } from "../types";
|
|
15
|
+
|
|
16
|
+
vi.mock("@/components/charts/lazy", () => ({
|
|
17
|
+
LazyVegaEmbed: vi.fn(() => null),
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
beforeAll(() => {
|
|
21
|
+
SetupMocks.resizeObserver();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe("ChartPanel", () => {
|
|
25
|
+
it.each([undefined, null, []] as const)(
|
|
26
|
+
"infers the full CSV through TablePanel without backend metadata (%s)",
|
|
27
|
+
async (fieldTypes) => {
|
|
28
|
+
vi.spyOn(vegaLoader, "load").mockResolvedValue(
|
|
29
|
+
"value,index\n,0\n2.5,1\n",
|
|
30
|
+
);
|
|
31
|
+
const store = createStore();
|
|
32
|
+
const id = cellId("csv-inference");
|
|
33
|
+
const tabName = getChartTabName(0, ChartType.BAR);
|
|
34
|
+
store.set(
|
|
35
|
+
tabsStorageAtom,
|
|
36
|
+
new Map([
|
|
37
|
+
[
|
|
38
|
+
id,
|
|
39
|
+
[
|
|
40
|
+
{
|
|
41
|
+
tabName,
|
|
42
|
+
chartType: ChartType.BAR,
|
|
43
|
+
config: {
|
|
44
|
+
general: {
|
|
45
|
+
xColumn: { field: "value", type: "number" },
|
|
46
|
+
yColumn: {
|
|
47
|
+
field: "index",
|
|
48
|
+
type: "integer",
|
|
49
|
+
aggregate: NONE_VALUE,
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
],
|
|
56
|
+
]),
|
|
57
|
+
);
|
|
58
|
+
const sample = [{ value: null }];
|
|
59
|
+
const SampleTable = (_props: { data: typeof sample }) => null;
|
|
60
|
+
const props: TablePanelProps = {
|
|
61
|
+
cellId: id,
|
|
62
|
+
data: sample,
|
|
63
|
+
dataTable: <SampleTable data={sample} />,
|
|
64
|
+
totalRows: 2,
|
|
65
|
+
columns: 1,
|
|
66
|
+
displayHeader: true,
|
|
67
|
+
fieldTypes: fieldTypes == null ? fieldTypes : [...fieldTypes],
|
|
68
|
+
rowHeaders: [["index", ["integer", "int64"]]],
|
|
69
|
+
getDataUrl: vi
|
|
70
|
+
.fn()
|
|
71
|
+
.mockResolvedValue({ data_url: "chart.csv", format: "csv" }),
|
|
72
|
+
};
|
|
73
|
+
const { getByRole } = render(
|
|
74
|
+
<Provider store={store}>
|
|
75
|
+
<Tooltip.Provider>
|
|
76
|
+
<TablePanel {...props} />
|
|
77
|
+
</Tooltip.Provider>
|
|
78
|
+
</Provider>,
|
|
79
|
+
);
|
|
80
|
+
fireEvent.click(getByRole("tab", { name: tabName }));
|
|
81
|
+
await waitFor(() => {
|
|
82
|
+
expect(vi.mocked(LazyVegaEmbed).mock.lastCall?.[0].spec).toEqual(
|
|
83
|
+
expect.objectContaining({
|
|
84
|
+
data: {
|
|
85
|
+
values: [
|
|
86
|
+
{ value: null, index: 0 },
|
|
87
|
+
{ value: 2.5, index: 1 },
|
|
88
|
+
],
|
|
89
|
+
},
|
|
90
|
+
}),
|
|
91
|
+
);
|
|
92
|
+
});
|
|
93
|
+
},
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
it("reloads CSV only when column types change, not their array identity", async () => {
|
|
97
|
+
vi.spyOn(vegaLoader, "load").mockResolvedValue("value\n001\n");
|
|
98
|
+
const getDataUrl = vi.fn().mockResolvedValue({
|
|
99
|
+
data_url: "chart.csv",
|
|
100
|
+
format: "csv",
|
|
101
|
+
});
|
|
102
|
+
const props: ComponentProps<typeof ChartPanel> = {
|
|
103
|
+
tableData: [{ value: "001" }],
|
|
104
|
+
chartConfig: {
|
|
105
|
+
general: {
|
|
106
|
+
xColumn: { field: "value", type: "string" },
|
|
107
|
+
yColumn: { field: "value", type: "integer", aggregate: NONE_VALUE },
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
chartType: ChartType.BAR,
|
|
111
|
+
saveChart: vi.fn(),
|
|
112
|
+
saveChartType: vi.fn(),
|
|
113
|
+
getDataUrl,
|
|
114
|
+
isLargeDataset: false,
|
|
115
|
+
};
|
|
116
|
+
const panel = (
|
|
117
|
+
fieldTypes: ComponentProps<typeof ChartPanel>["fieldTypes"],
|
|
118
|
+
) => (
|
|
119
|
+
<Tooltip.Provider>
|
|
120
|
+
<ChartPanel {...props} fieldTypes={fieldTypes} />
|
|
121
|
+
</Tooltip.Provider>
|
|
122
|
+
);
|
|
123
|
+
const expectValues = async (value: string | number) => {
|
|
124
|
+
await waitFor(() => {
|
|
125
|
+
expect(vi.mocked(LazyVegaEmbed).mock.lastCall?.[0].spec).toEqual(
|
|
126
|
+
expect.objectContaining({ data: { values: [{ value }] } }),
|
|
127
|
+
);
|
|
128
|
+
});
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const { rerender } = render(panel([["value", ["string", "object"]]]));
|
|
132
|
+
await expectValues("001");
|
|
133
|
+
expect(getDataUrl).toHaveBeenCalledTimes(1);
|
|
134
|
+
|
|
135
|
+
rerender(panel([["value", ["string", "object"]]]));
|
|
136
|
+
await expectValues("001");
|
|
137
|
+
expect(getDataUrl).toHaveBeenCalledTimes(1);
|
|
138
|
+
|
|
139
|
+
rerender(panel([["value", ["integer", "int64"]]]));
|
|
140
|
+
await expectValues(1);
|
|
141
|
+
expect(getDataUrl).toHaveBeenCalledTimes(2);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it("uses column types to parse numeric CSV values without coercing text", async () => {
|
|
145
|
+
vi.spyOn(vegaLoader, "load").mockResolvedValue(
|
|
146
|
+
"a.b,n,label,day,timestamp,active,duration\ninf,1,inf,2024-01-01,2024-01-01T12:00:00Z,True,1 days\n-inf,2,001,2024-01-02,2024-01-02T12:00:00Z,False,2 days\n2.5,3,2024-01-03,2024-01-03,2024-01-03T12:00:00Z,True,3 days\n,4,,,,,\n",
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
render(
|
|
150
|
+
<Tooltip.Provider>
|
|
151
|
+
<ChartPanel
|
|
152
|
+
tableData={[{ "a.b": 1, n: 1 }]}
|
|
153
|
+
chartConfig={{
|
|
154
|
+
general: {
|
|
155
|
+
xColumn: { field: "a.b", type: "number" },
|
|
156
|
+
yColumn: { field: "n", type: "integer", aggregate: NONE_VALUE },
|
|
157
|
+
},
|
|
158
|
+
}}
|
|
159
|
+
chartType={ChartType.BAR}
|
|
160
|
+
saveChart={vi.fn()}
|
|
161
|
+
saveChartType={vi.fn()}
|
|
162
|
+
getDataUrl={vi.fn().mockResolvedValue({
|
|
163
|
+
data_url: "chart.csv",
|
|
164
|
+
format: "csv",
|
|
165
|
+
})}
|
|
166
|
+
fieldTypes={[
|
|
167
|
+
["a.b", ["number", "float64"]],
|
|
168
|
+
["n", ["integer", "int64"]],
|
|
169
|
+
["label", ["string", "object"]],
|
|
170
|
+
["day", ["date", "date"]],
|
|
171
|
+
["timestamp", ["datetime", "datetime64[ns]"]],
|
|
172
|
+
["active", ["boolean", "bool"]],
|
|
173
|
+
["duration", ["unknown", "timedelta64[ns]"]],
|
|
174
|
+
]}
|
|
175
|
+
isLargeDataset={false}
|
|
176
|
+
/>
|
|
177
|
+
</Tooltip.Provider>,
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
await waitFor(() => {
|
|
181
|
+
expect(vi.mocked(LazyVegaEmbed).mock.lastCall?.[0].spec).toEqual(
|
|
182
|
+
expect.objectContaining({
|
|
183
|
+
data: {
|
|
184
|
+
values: [
|
|
185
|
+
{
|
|
186
|
+
"a.b": Infinity,
|
|
187
|
+
n: 1,
|
|
188
|
+
label: "inf",
|
|
189
|
+
day: new Date("2024-01-01"),
|
|
190
|
+
timestamp: new Date("2024-01-01T12:00:00Z"),
|
|
191
|
+
active: true,
|
|
192
|
+
duration: "1 days",
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
"a.b": -Infinity,
|
|
196
|
+
n: 2,
|
|
197
|
+
label: "001",
|
|
198
|
+
day: new Date("2024-01-02"),
|
|
199
|
+
timestamp: new Date("2024-01-02T12:00:00Z"),
|
|
200
|
+
active: false,
|
|
201
|
+
duration: "2 days",
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
"a.b": 2.5,
|
|
205
|
+
n: 3,
|
|
206
|
+
label: "2024-01-03",
|
|
207
|
+
day: new Date("2024-01-03"),
|
|
208
|
+
timestamp: new Date("2024-01-03T12:00:00Z"),
|
|
209
|
+
active: true,
|
|
210
|
+
duration: "3 days",
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
"a.b": null,
|
|
214
|
+
n: 4,
|
|
215
|
+
label: null,
|
|
216
|
+
day: "",
|
|
217
|
+
timestamp: "",
|
|
218
|
+
active: null,
|
|
219
|
+
duration: "",
|
|
220
|
+
},
|
|
221
|
+
],
|
|
222
|
+
},
|
|
223
|
+
}),
|
|
224
|
+
);
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
it.each([true, false])(
|
|
229
|
+
"keeps dotted CSV columns aligned with the chart encodings (schema: %s)",
|
|
230
|
+
async (hasSchema) => {
|
|
231
|
+
vi.spyOn(vegaLoader, "load").mockResolvedValue("a.b,n\n1,1\n2,2\n3,3\n");
|
|
232
|
+
|
|
233
|
+
render(
|
|
234
|
+
<Tooltip.Provider>
|
|
235
|
+
<ChartPanel
|
|
236
|
+
tableData={[{ "a.b": 1, n: 1 }]}
|
|
237
|
+
chartConfig={{
|
|
238
|
+
general: {
|
|
239
|
+
xColumn: { field: "a.b", type: "number" },
|
|
240
|
+
yColumn: {
|
|
241
|
+
field: "n",
|
|
242
|
+
type: "number",
|
|
243
|
+
aggregate: NONE_VALUE,
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
}}
|
|
247
|
+
chartType={ChartType.BAR}
|
|
248
|
+
saveChart={vi.fn()}
|
|
249
|
+
saveChartType={vi.fn()}
|
|
250
|
+
getDataUrl={vi.fn().mockResolvedValue({
|
|
251
|
+
data_url: "chart.csv",
|
|
252
|
+
format: "csv",
|
|
253
|
+
})}
|
|
254
|
+
fieldTypes={
|
|
255
|
+
hasSchema
|
|
256
|
+
? [
|
|
257
|
+
["a.b", ["number", "float64"]],
|
|
258
|
+
["n", ["integer", "int64"]],
|
|
259
|
+
]
|
|
260
|
+
: undefined
|
|
261
|
+
}
|
|
262
|
+
isLargeDataset={false}
|
|
263
|
+
/>
|
|
264
|
+
</Tooltip.Provider>,
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
await waitFor(() => {
|
|
268
|
+
expect(vi.mocked(LazyVegaEmbed).mock.lastCall?.[0].spec).toEqual(
|
|
269
|
+
expect.objectContaining({
|
|
270
|
+
data: {
|
|
271
|
+
values: [
|
|
272
|
+
{ "a.b": 1, n: 1 },
|
|
273
|
+
{ "a.b": 2, n: 2 },
|
|
274
|
+
{ "a.b": 3, n: 3 },
|
|
275
|
+
],
|
|
276
|
+
},
|
|
277
|
+
encoding: expect.objectContaining({
|
|
278
|
+
x: expect.objectContaining({ field: "a\\.b" }),
|
|
279
|
+
y: expect.objectContaining({ field: "n" }),
|
|
280
|
+
}),
|
|
281
|
+
}),
|
|
282
|
+
);
|
|
283
|
+
});
|
|
284
|
+
},
|
|
285
|
+
);
|
|
286
|
+
});
|
|
@@ -23,9 +23,11 @@ import { Form } from "@/components/ui/form";
|
|
|
23
23
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
|
24
24
|
import type { CellId } from "@/core/cells/ids";
|
|
25
25
|
import { useAsyncData } from "@/hooks/useAsyncData";
|
|
26
|
+
import { useDeepCompareMemoize } from "@/hooks/useDeepCompareMemoize";
|
|
26
27
|
import { useDebouncedCallback } from "@/hooks/useDebounce";
|
|
27
28
|
import type { GetDataUrl } from "@/plugins/impl/DataTablePlugin";
|
|
28
29
|
import { vegaLoadData } from "@/plugins/impl/vega/loader";
|
|
30
|
+
import { getVegaFieldTypes } from "@/plugins/impl/vega/utils";
|
|
29
31
|
import { useTheme } from "@/theme/useTheme";
|
|
30
32
|
import { uniqueBy } from "@/utils/arrays";
|
|
31
33
|
import { inferFieldTypes } from "../columns";
|
|
@@ -284,6 +286,7 @@ export const TablePanel: React.FC<TablePanelProps> = ({
|
|
|
284
286
|
saveChart={saveChart}
|
|
285
287
|
saveChartType={saveChartType}
|
|
286
288
|
getDataUrl={getDataUrl}
|
|
289
|
+
hasSchema={Boolean(fieldTypes?.length)}
|
|
287
290
|
fieldTypes={mergeIndexFields(
|
|
288
291
|
fieldTypes ?? inferFieldTypes(dataTable.props.data),
|
|
289
292
|
rowHeaders,
|
|
@@ -307,6 +310,7 @@ export const ChartPanel: React.FC<{
|
|
|
307
310
|
saveChartType: (chartType: ChartType) => void;
|
|
308
311
|
getDataUrl?: GetDataUrl;
|
|
309
312
|
fieldTypes?: FieldTypesWithExternalType | null;
|
|
313
|
+
hasSchema?: boolean;
|
|
310
314
|
isLargeDataset: boolean;
|
|
311
315
|
}> = ({
|
|
312
316
|
tableData,
|
|
@@ -316,6 +320,7 @@ export const ChartPanel: React.FC<{
|
|
|
316
320
|
saveChartType,
|
|
317
321
|
getDataUrl,
|
|
318
322
|
fieldTypes,
|
|
323
|
+
hasSchema = Boolean(fieldTypes?.length),
|
|
319
324
|
isLargeDataset,
|
|
320
325
|
}) => {
|
|
321
326
|
const { theme } = useTheme();
|
|
@@ -331,6 +336,8 @@ export const ChartPanel: React.FC<{
|
|
|
331
336
|
const [renderLargeCharts, setRenderLargeCharts] = useState(!isLargeDataset);
|
|
332
337
|
|
|
333
338
|
const { ref: chartContainerRef } = useResizeObserver();
|
|
339
|
+
// Sample-inferred types are useful for controls, but not full CSV parsing.
|
|
340
|
+
const csvFieldTypes = useDeepCompareMemoize(hasSchema ? fieldTypes : null);
|
|
334
341
|
|
|
335
342
|
const { data, isPending, error } = useAsyncData(async () => {
|
|
336
343
|
if (!getDataUrl || tableData.length === 0 || !renderLargeCharts) {
|
|
@@ -342,20 +349,26 @@ export const ChartPanel: React.FC<{
|
|
|
342
349
|
return response.data_url;
|
|
343
350
|
}
|
|
344
351
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
352
|
+
let format: Parameters<typeof vegaLoadData>[1];
|
|
353
|
+
if (response.format === "arrow") {
|
|
354
|
+
format = { type: "arrow" };
|
|
355
|
+
} else if (response.format === "json") {
|
|
356
|
+
format = { type: "json" };
|
|
357
|
+
} else {
|
|
358
|
+
format = {
|
|
359
|
+
type: "csv",
|
|
360
|
+
parse: getVegaFieldTypes(
|
|
361
|
+
csvFieldTypes &&
|
|
362
|
+
Object.fromEntries(
|
|
363
|
+
csvFieldTypes.map(([name, [type]]) => [name, type]),
|
|
364
|
+
),
|
|
365
|
+
{ parseDates: true },
|
|
366
|
+
),
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
return vegaLoadData(response.data_url, format);
|
|
357
370
|
// Re-run when the data table changes
|
|
358
|
-
}, [tableData, renderLargeCharts]);
|
|
371
|
+
}, [tableData, renderLargeCharts, csvFieldTypes]);
|
|
359
372
|
|
|
360
373
|
const formValues = form.watch();
|
|
361
374
|
|
|
@@ -40,7 +40,12 @@ const ContributeSnippetModal: React.FC<{
|
|
|
40
40
|
<DialogDescription>
|
|
41
41
|
Have a useful snippet you want to share with the community? Make a
|
|
42
42
|
pull request{" "}
|
|
43
|
-
<a
|
|
43
|
+
<a
|
|
44
|
+
href={Constants.githubPage}
|
|
45
|
+
target="_blank"
|
|
46
|
+
rel="noreferrer"
|
|
47
|
+
className="underline"
|
|
48
|
+
>
|
|
44
49
|
on GitHub
|
|
45
50
|
</a>
|
|
46
51
|
.
|
|
@@ -762,7 +762,7 @@ const EditableCellComponent = ({
|
|
|
762
762
|
href="https://links.marimo.app/reusable-definitions"
|
|
763
763
|
target="_blank"
|
|
764
764
|
className="hover:underline text-muted-foreground text-xs font-bold"
|
|
765
|
-
rel="
|
|
765
|
+
rel="noreferrer"
|
|
766
766
|
>
|
|
767
767
|
reusable
|
|
768
768
|
</a>
|
|
@@ -801,7 +801,7 @@ const EditableCellComponent = ({
|
|
|
801
801
|
<a
|
|
802
802
|
href="https://links.marimo.app/reusable-definitions"
|
|
803
803
|
target="_blank"
|
|
804
|
-
rel="
|
|
804
|
+
rel="noreferrer"
|
|
805
805
|
>
|
|
806
806
|
<HelpCircleIcon
|
|
807
807
|
size={16}
|
|
@@ -13,8 +13,10 @@ export interface Cursor {
|
|
|
13
13
|
export class TerminalBuffer {
|
|
14
14
|
private lines: string[] = [""];
|
|
15
15
|
private cursor: Cursor = { row: 0, col: 0 };
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
private static readonly ESCAPE_REGEX = new RegExp(
|
|
17
|
+
String.raw`\u001B\[([0-9;]*)([A-DJKH])`,
|
|
18
|
+
"u",
|
|
19
|
+
);
|
|
18
20
|
|
|
19
21
|
/** Ensure the internal lines array is large enough. */
|
|
20
22
|
private ensureLine(row: number) {
|
|
@@ -180,8 +182,10 @@ export class TerminalBuffer {
|
|
|
180
182
|
*/
|
|
181
183
|
export class AnsiParser {
|
|
182
184
|
// Matches both CSI sequences (ESC[...letter) and other escape sequences like character set selection (ESC(B)
|
|
183
|
-
|
|
184
|
-
|
|
185
|
+
private ESC_REGEX = new RegExp(
|
|
186
|
+
String.raw`\u001B(?:\[[0-9;]*[A-Za-z]|\([0-9A-Za-z])`,
|
|
187
|
+
"gu",
|
|
188
|
+
);
|
|
185
189
|
|
|
186
190
|
parse(input: string): { type: "text" | "escape"; value: string }[] {
|
|
187
191
|
const tokens: { type: "text" | "escape"; value: string }[] = [];
|
|
@@ -256,6 +256,7 @@ export const pipInstallReplacer: Replacer = (domNode: DOMNode) => {
|
|
|
256
256
|
);
|
|
257
257
|
}
|
|
258
258
|
|
|
259
|
+
// oxlint-disable-next-line react/jsx-no-useless-fragment -- html-react-parser replacers must return a React element.
|
|
259
260
|
return <>{segments}</>;
|
|
260
261
|
};
|
|
261
262
|
|
|
@@ -283,6 +284,7 @@ export const urlReplacer: Replacer = (domNode: DOMNode) => {
|
|
|
283
284
|
return undefined;
|
|
284
285
|
}
|
|
285
286
|
|
|
287
|
+
// oxlint-disable-next-line react/jsx-no-useless-fragment -- html-react-parser replacers must return a React element.
|
|
286
288
|
return <>{result}</>;
|
|
287
289
|
};
|
|
288
290
|
|
|
@@ -331,5 +333,5 @@ export const RenderTextWithLinks = ({ text }: { text: string }) => {
|
|
|
331
333
|
);
|
|
332
334
|
}, [text]);
|
|
333
335
|
|
|
334
|
-
return
|
|
336
|
+
return content;
|
|
335
337
|
};
|
|
@@ -170,6 +170,7 @@ export const ResourceLinks: React.FC = () => {
|
|
|
170
170
|
key={resource.title}
|
|
171
171
|
href={resource.url}
|
|
172
172
|
target="_blank"
|
|
173
|
+
rel="noreferrer"
|
|
173
174
|
aria-label={`${resource.title}: ${resource.description}`}
|
|
174
175
|
className="flex items-start gap-3 py-3 px-3 rounded-lg border hover:bg-accent/20 transition-colors shadow-xs"
|
|
175
176
|
>
|
|
@@ -50,6 +50,7 @@ const Watermark = () => {
|
|
|
50
50
|
<a
|
|
51
51
|
href={Constants.githubPage}
|
|
52
52
|
target="_blank"
|
|
53
|
+
rel="noreferrer"
|
|
53
54
|
className="text-sm text-(--grass-11) font-bold tracking-wide transition-colors bg-(--grass-4) hover:bg-(--grass-5) border-t border-l border-(--grass-8) px-3 py-1 rounded-tl-md flex items-center gap-2"
|
|
54
55
|
>
|
|
55
56
|
<span>made with marimo</span>
|
|
@@ -74,6 +74,7 @@ export const ShareStaticNotebookModal: React.FC<{
|
|
|
74
74
|
<a
|
|
75
75
|
href={Constants.issuesPage}
|
|
76
76
|
target="_blank"
|
|
77
|
+
rel="noreferrer"
|
|
77
78
|
className="underline"
|
|
78
79
|
>
|
|
79
80
|
GitHub
|
|
@@ -102,7 +103,7 @@ export const ShareStaticNotebookModal: React.FC<{
|
|
|
102
103
|
<DialogDescription>
|
|
103
104
|
You can publish a static, non-interactive version of this notebook
|
|
104
105
|
to the public web. We will create a link for you that lives on{" "}
|
|
105
|
-
<a href={BASE_URL} target="_blank">
|
|
106
|
+
<a href={BASE_URL} target="_blank" rel="noreferrer">
|
|
106
107
|
{BASE_URL}
|
|
107
108
|
</a>
|
|
108
109
|
.
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
-
/* oxlint-disable react/jsx-no-comment-textnodes */
|
|
3
|
-
/* oxlint-disable react/jsx-no-target-blank */
|
|
4
2
|
|
|
5
3
|
import { useAtomValue } from "jotai";
|
|
6
4
|
import { CopyIcon, DownloadIcon } from "lucide-react";
|
|
@@ -49,6 +47,7 @@ export const StaticBanner: React.FC = () => {
|
|
|
49
47
|
<a
|
|
50
48
|
href={Constants.githubPage}
|
|
51
49
|
target="_blank"
|
|
50
|
+
rel="noreferrer"
|
|
52
51
|
className="text-(--sky-11) font-medium underline"
|
|
53
52
|
>
|
|
54
53
|
marimo
|
|
@@ -93,6 +92,7 @@ const StaticBannerDialog = ({ code }: { code: string }) => {
|
|
|
93
92
|
<a
|
|
94
93
|
href={Constants.githubPage}
|
|
95
94
|
target="_blank"
|
|
95
|
+
rel="noreferrer"
|
|
96
96
|
className="text-(--sky-11) hover:underline font-medium"
|
|
97
97
|
>
|
|
98
98
|
marimo
|
|
@@ -21,7 +21,12 @@ export const ExternalLink = ({
|
|
|
21
21
|
children: React.ReactNode;
|
|
22
22
|
}) => {
|
|
23
23
|
return (
|
|
24
|
-
<a
|
|
24
|
+
<a
|
|
25
|
+
href={href}
|
|
26
|
+
target="_blank"
|
|
27
|
+
rel="noreferrer"
|
|
28
|
+
className="text-link hover:underline"
|
|
29
|
+
>
|
|
25
30
|
{children}
|
|
26
31
|
</a>
|
|
27
32
|
);
|