@marimo-team/islands 0.24.3-dev32 → 0.24.3-dev36
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-BLO7UDpo.js → ConnectedDataExplorerComponent-DeiKXaGB.js} +4 -4
- package/dist/{ErrorBoundary-B7uSdDUm.js → ErrorBoundary-B3IKS4qY.js} +1 -1
- package/dist/{_baseMerge-Bnytch8P.js → _baseMerge-BiGRwuqv.js} +1 -1
- package/dist/{abnfDiagram-VCTEODGH-DCb2K_RB.js → abnfDiagram-VCTEODGH-DKNGRyhn.js} +2 -2
- package/dist/{any-language-editor-C0dftc2Z.js → any-language-editor-BdvsE_1G.js} +2 -2
- package/dist/{architectureDiagram-5GKGNRK7-Bb_n642s.js → architectureDiagram-5GKGNRK7-C9oCEyV-.js} +1 -1
- package/dist/{chat-ui-DiMxe-GH.js → chat-ui-D15itIKJ.js} +8 -8
- package/dist/{chunk-IMKFNOWR-Du9BN2Ul.js → chunk-IMKFNOWR-ByL0a1rh.js} +1 -1
- package/dist/{chunk-SHT3W25Y-tUelricH.js → chunk-SHT3W25Y-D1Nb5X1c.js} +1 -1
- package/dist/{chunk-SVP7TREG-DK3egkfQ.js → chunk-SVP7TREG-BShKxcfD.js} +1 -1
- package/dist/{chunk-TICWLB2K-DRANTdnp.js → chunk-TICWLB2K-zpDbKPt8.js} +1 -1
- package/dist/{classDiagram-ZZMXUADV-7wCSVH7o.js → classDiagram-ZZMXUADV-qcsoIIQw.js} +2 -2
- package/dist/{classDiagram-v2-VYDZK3BY-QwnvcnhB.js → classDiagram-v2-VYDZK3BY-CAgMct5x.js} +2 -2
- package/dist/{common-Dxt3H8za.js → common-CWnP5Rod.js} +7921 -11807
- package/dist/{cynefinDiagram-5FMLGOSQ-CB7O4IZB.js → cynefinDiagram-5FMLGOSQ-BzBXigwN.js} +1 -1
- package/dist/{dagre-DfYd3btw.js → dagre-BGSfXNEb.js} +21 -21
- package/dist/{dagre-GXQ25YYZ-C-a95ISW.js → dagre-GXQ25YYZ-B-75J283.js} +2 -2
- package/dist/{data-grid-overlay-editor-C5eUZVTo.js → data-grid-overlay-editor-DMu5X3F1.js} +1 -1
- package/dist/{diagram-S7CK7UJ4-BsKLYQaH.js → diagram-S7CK7UJ4-Bzr5oWc0.js} +1 -1
- package/dist/{diagram-UQ7AKVKN-ppYdvuTN.js → diagram-UQ7AKVKN-CVIH_HJ_.js} +1 -1
- package/dist/{diagram-VX7I27RA-CAMK2l0E.js → diagram-VX7I27RA-BmoMwYLC.js} +1 -1
- package/dist/{diagram-Z3DM3KII-CxQ7MggT.js → diagram-Z3DM3KII-BW1w6mnj.js} +1 -1
- package/dist/{dropdown-menu-C73aTomr.js → dropdown-menu-WMrB1UnI.js} +1 -1
- package/dist/{ebnfDiagram-PWID7BFC-DzybELvk.js → ebnfDiagram-PWID7BFC-90Q6gkC0.js} +2 -2
- package/dist/{erDiagram-RLTQ6QDP-BVxV97ld.js → erDiagram-RLTQ6QDP-BZkLy3TR.js} +1 -1
- package/dist/{esm-blALgIVy.js → esm-qM9GvHZi.js} +293 -307
- package/dist/{flowDiagram-HODETNUW-BKvXsWYX.js → flowDiagram-HODETNUW-CD1F_wj5.js} +2 -2
- package/dist/{formats-vDAoRpYs.js → formats-B-gajYGs.js} +1 -1
- package/dist/{glide-data-editor-Bcy8iaZa.js → glide-data-editor-DzFEatOq.js} +2218 -2209
- package/dist/{glide-utils-BMnFm0vZ.js → glide-utils-4WmLlefP.js} +11 -11
- package/dist/{html-to-image-mVIbKn_Y.js → html-to-image-CZm_DMIE.js} +15 -15
- package/dist/{infoDiagram-27XIBGKW-B_ucqo-U.js → infoDiagram-27XIBGKW-t0zIIRVa.js} +1 -1
- package/dist/{ishikawaDiagram-5VMMS53U-BZFx9b-o.js → ishikawaDiagram-5VMMS53U-DtQUIYgx.js} +1 -1
- package/dist/{kanban-definition-UXKFOSKX-BpIfcu3G.js → kanban-definition-UXKFOSKX-CUMmwuNB.js} +1 -1
- package/dist/main.js +5274 -5283
- package/dist/{mermaid-B1Jb9HfY.js → mermaid-CMLxoJ0m.js} +34 -34
- package/dist/{mindmap-definition-YA3MSWOX-CmpAfv8K.js → mindmap-definition-YA3MSWOX-DnbkiQla.js} +1 -1
- package/dist/{number-overlay-editor-CFofZaOi.js → number-overlay-editor-lGqRHkFt.js} +1 -1
- package/dist/{extends-Do6pRuJa.js → objectWithoutPropertiesLoose-BbiTb8zy.js} +12 -1
- package/dist/{pegDiagram-XKGWAZYB-DO-0Q8SE.js → pegDiagram-XKGWAZYB-qgqAYMdv.js} +2 -2
- package/dist/{pick-B7vX8zxG.js → pick-CJeoxYOS.js} +1 -1
- package/dist/{pieDiagram-E7YTZNPT-B1MbuA_g.js → pieDiagram-E7YTZNPT-DJUB6w6B.js} +1 -1
- package/dist/{process-output-Cr6U5ULK.js → process-output-DuupNR0m.js} +1 -1
- package/dist/{railroadDiagram-O6MQD6OU-DPVOQUXC.js → railroadDiagram-O6MQD6OU-C0dtw4sR.js} +2 -2
- package/dist/{requirementDiagram-BXWQKSXE-CC7wZ-tM.js → requirementDiagram-BXWQKSXE-DqaTsUuC.js} +1 -1
- package/dist/{reveal-component-Dli5fs0u.js → reveal-component-SdzpGkJs.js} +486 -486
- package/dist/{sequenceDiagram-WJ2MYXX4-DomSyoJI.js → sequenceDiagram-WJ2MYXX4-D2NyWGuu.js} +1 -1
- package/dist/{spec-g740fOWu.js → spec-BiD5y7ft.js} +1 -1
- package/dist/{stateDiagram-D77RDMKH-SQLLjdtF.js → stateDiagram-D77RDMKH-JChw35bc.js} +3 -3
- package/dist/{stateDiagram-v2-MP3YSRHH-Dup4KkIr.js → stateDiagram-v2-MP3YSRHH-BfVY7OxA.js} +2 -2
- package/dist/style.css +1 -1
- package/dist/{swimlanes-42K2YHIH-ZXbhyGv6.js → swimlanes-42K2YHIH-eAB6utyY.js} +1 -1
- package/dist/{swimlanesDiagram-VR7AAH4N-CluwxV0g.js → swimlanesDiagram-VR7AAH4N-Cnpd2YtA.js} +2 -2
- package/dist/{timeline-definition-24CTP7MA-B8i6B9RR.js → timeline-definition-24CTP7MA-DlnODX3U.js} +1 -1
- package/dist/{toDate-C3JOXlJT.js → toDate-0cxDmdsp.js} +1 -1
- package/dist/{useAsyncData-C0f7qnnN.js → useAsyncData-DCIhDLCe.js} +1 -1
- package/dist/{useDeepCompareMemoize-QCa0HbWB.js → useDeepCompareMemoize-CLaZB7Gc.js} +1 -1
- package/dist/{useLifecycle-BGRBkTFT.js → useLifecycle-C1ViBACC.js} +1 -1
- package/dist/{useTheme-iQ0VoptU.js → useTheme-Da9LJvrQ.js} +1 -1
- package/dist/{vega-component-DRI4BDLQ.js → vega-component-Bt6d8B4Q.js} +5 -5
- package/dist/{vennDiagram-4TSXK5OY-CS7lHhxI.js → vennDiagram-4TSXK5OY-B_m5KxUg.js} +1 -1
- package/dist/{wardleyDiagram-VM6X3IG4-BSm-L8JI.js → wardleyDiagram-VM6X3IG4-Dw-Lgv3D.js} +1 -1
- package/dist/{xychartDiagram-S5SC5T6Z-8fKCJGrJ.js → xychartDiagram-S5SC5T6Z-BjfRms1e.js} +1 -1
- package/package.json +3 -6
- package/src/components/data-table/column-summary/chart-spec-model.tsx +1 -1
- package/src/components/data-table/columns.tsx +64 -67
- package/src/components/editor/header/filename-input.tsx +2 -0
- package/src/components/editor/output/JsonOutput.tsx +182 -632
- package/src/components/editor/output/Outputs.css +49 -17
- package/src/components/editor/output/__tests__/JsonOutput-interactions.test.tsx +254 -0
- package/src/components/editor/output/__tests__/JsonOutput-mimetype.test.tsx +41 -46
- package/src/components/editor/output/__tests__/{json-output.test.ts → json-output-formatting.test.ts} +113 -75
- package/src/components/editor/output/__tests__/json-output-pagination.test.ts +252 -0
- package/src/components/editor/output/json-output/formatting.ts +167 -0
- package/src/components/editor/output/json-output/pagination.ts +155 -0
- package/src/components/editor/output/json-output/renderers.tsx +196 -0
- package/src/components/editor/output/json-output/themes.ts +52 -0
- package/src/hooks/useDebounce.ts +2 -0
- package/src/plugins/core/BadPlugin.tsx +3 -14
- package/src/plugins/core/registerReactComponent.tsx +0 -1
- package/src/plugins/impl/FormPlugin.tsx +2 -0
- package/src/plugins/impl/MatrixPlugin.tsx +2 -0
- package/src/plugins/impl/RangeSliderPlugin.tsx +2 -0
- package/src/plugins/impl/SliderPlugin.tsx +2 -0
- package/src/plugins/layout/JsonOutputPlugin.tsx +7 -10
- package/dist/emotion-is-prop-valid.esm-M57VsT8j.js +0 -14
- package/src/components/editor/output/EmotionCacheProvider.tsx +0 -32
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
import { isPlainObject } from "lodash-es";
|
|
3
|
+
|
|
4
|
+
export const PAGE_SIZE = 30;
|
|
5
|
+
|
|
6
|
+
interface Page {
|
|
7
|
+
path: string;
|
|
8
|
+
remaining: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface Options {
|
|
12
|
+
limits: Record<string, number>;
|
|
13
|
+
pageSize: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function paginateJson(data: object, { limits, pageSize }: Options) {
|
|
17
|
+
const originals = new WeakMap<object, object>();
|
|
18
|
+
const pages = new Map<symbol, Page>();
|
|
19
|
+
|
|
20
|
+
function visit(node: object, path: string[]): object {
|
|
21
|
+
if (!Array.isArray(node) && !isPlainObject(node)) {
|
|
22
|
+
return node;
|
|
23
|
+
}
|
|
24
|
+
const pathKey = JSON.stringify(path);
|
|
25
|
+
const limit = limits[pathKey] ?? pageSize;
|
|
26
|
+
const keys = Array.isArray(node)
|
|
27
|
+
? Array.from({ length: Math.min(node.length, limit) }, (_, i) =>
|
|
28
|
+
String(i),
|
|
29
|
+
)
|
|
30
|
+
: Object.keys(node).slice(0, limit);
|
|
31
|
+
const count = Array.isArray(node) ? node.length : Object.keys(node).length;
|
|
32
|
+
const result: object = Array.isArray(node) ? [] : Object.create(null);
|
|
33
|
+
originals.set(result, node);
|
|
34
|
+
|
|
35
|
+
for (const key of keys) {
|
|
36
|
+
// Materialize only the branches the viewer reads, including after expansion.
|
|
37
|
+
Object.defineProperty(result, key, {
|
|
38
|
+
enumerable: true,
|
|
39
|
+
configurable: true,
|
|
40
|
+
get() {
|
|
41
|
+
const original: unknown = Reflect.get(node, key);
|
|
42
|
+
const value =
|
|
43
|
+
original !== null && typeof original === "object"
|
|
44
|
+
? visit(original, [...path, key])
|
|
45
|
+
: original;
|
|
46
|
+
Object.defineProperty(result, key, {
|
|
47
|
+
value,
|
|
48
|
+
enumerable: true,
|
|
49
|
+
configurable: true,
|
|
50
|
+
});
|
|
51
|
+
return value;
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
if (count > limit) {
|
|
56
|
+
const marker = Symbol("show more");
|
|
57
|
+
pages.set(marker, { remaining: count - limit, path: pathKey });
|
|
58
|
+
let key = Array.isArray(node) ? String(limit) : "";
|
|
59
|
+
while (Object.hasOwn(result, key)) {
|
|
60
|
+
key += "\0";
|
|
61
|
+
}
|
|
62
|
+
Object.defineProperty(result, key, { value: marker, enumerable: true });
|
|
63
|
+
}
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
data: visit(data, []),
|
|
69
|
+
getOriginal(value: unknown): unknown {
|
|
70
|
+
return value !== null && typeof value === "object"
|
|
71
|
+
? (originals.get(value) ?? value)
|
|
72
|
+
: value;
|
|
73
|
+
},
|
|
74
|
+
getPage(value: unknown): Page | undefined {
|
|
75
|
+
return typeof value === "symbol" ? pages.get(value) : undefined;
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function estimateExpandedLines(
|
|
81
|
+
node: unknown,
|
|
82
|
+
cap: number,
|
|
83
|
+
depth = 0,
|
|
84
|
+
): number {
|
|
85
|
+
if (
|
|
86
|
+
depth > 4 ||
|
|
87
|
+
node === null ||
|
|
88
|
+
node === undefined ||
|
|
89
|
+
typeof node !== "object"
|
|
90
|
+
) {
|
|
91
|
+
return 1;
|
|
92
|
+
}
|
|
93
|
+
const children: unknown[] = Array.isArray(node) ? node : Object.values(node);
|
|
94
|
+
let lines = 0;
|
|
95
|
+
for (let i = 0; i < children.length && lines < cap; i++) {
|
|
96
|
+
const child = children[i];
|
|
97
|
+
if (child === null || child === undefined || typeof child !== "object") {
|
|
98
|
+
lines++;
|
|
99
|
+
} else {
|
|
100
|
+
const childLen = Array.isArray(child)
|
|
101
|
+
? child.length
|
|
102
|
+
: Object.keys(child).length;
|
|
103
|
+
lines +=
|
|
104
|
+
childLen <= 8
|
|
105
|
+
? estimateExpandedLines(child, cap - lines, depth + 1)
|
|
106
|
+
: 1;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return lines;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function shouldExpandNode(
|
|
113
|
+
_isExpanded: boolean,
|
|
114
|
+
{ value, level }: { value?: object; level: number },
|
|
115
|
+
): boolean {
|
|
116
|
+
if (level <= 1) {
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
if (level > 5) {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
if (value === null || value === undefined || typeof value !== "object") {
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
const len = Array.isArray(value) ? value.length : Object.keys(value).length;
|
|
126
|
+
if (len === 0) {
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
if (len > 200) {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
return estimateExpandedLines(value, 150) <= 120;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function determineMaxDisplayLength(data: unknown): number | undefined {
|
|
136
|
+
if (!Array.isArray(data)) {
|
|
137
|
+
return undefined;
|
|
138
|
+
}
|
|
139
|
+
const sample = data.slice(0, 15);
|
|
140
|
+
let maxLength = 0;
|
|
141
|
+
for (const el of sample) {
|
|
142
|
+
if (Array.isArray(el)) {
|
|
143
|
+
for (const next of el.slice(0, 5)) {
|
|
144
|
+
if (Array.isArray(next)) {
|
|
145
|
+
return 5;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
maxLength = Math.max(maxLength, el.length);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (maxLength <= 20) {
|
|
152
|
+
return undefined;
|
|
153
|
+
}
|
|
154
|
+
return maxLength >= 50 ? 5 : 10;
|
|
155
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import type { OutputMessage } from "@/core/kernel/messages";
|
|
4
|
+
import { isUrl } from "@/utils/urls";
|
|
5
|
+
import { OutputRenderer } from "../../Output";
|
|
6
|
+
import { HtmlOutput } from "../HtmlOutput";
|
|
7
|
+
import { ImageOutput } from "../ImageOutput";
|
|
8
|
+
import { JsonOutput } from "../JsonOutput";
|
|
9
|
+
import { VideoOutput } from "../VideoOutput";
|
|
10
|
+
import {
|
|
11
|
+
decodePythonKey,
|
|
12
|
+
formatTuplePayload,
|
|
13
|
+
formatSetPayload,
|
|
14
|
+
formatFrozensetPayload,
|
|
15
|
+
splitLeaf,
|
|
16
|
+
} from "./formatting";
|
|
17
|
+
|
|
18
|
+
export const COLLAPSED_TEXT_LENGTH = 100;
|
|
19
|
+
|
|
20
|
+
// Keep long text and quotes wrapping together.
|
|
21
|
+
/* oxlint-disable jsx-a11y/prefer-tag-over-role */
|
|
22
|
+
const CollapsibleTextOutput: React.FC<{ text: string }> = ({ text }) => {
|
|
23
|
+
const [collapsed, setCollapsed] = useState(true);
|
|
24
|
+
if (text.length <= COLLAPSED_TEXT_LENGTH) {
|
|
25
|
+
return <span className="break-all">{text}</span>;
|
|
26
|
+
}
|
|
27
|
+
return (
|
|
28
|
+
<span
|
|
29
|
+
className="cursor-pointer hover:opacity-90 break-all"
|
|
30
|
+
role="button"
|
|
31
|
+
tabIndex={0}
|
|
32
|
+
aria-expanded={!collapsed}
|
|
33
|
+
onClick={() => setCollapsed(!collapsed)}
|
|
34
|
+
onKeyDown={(event) => {
|
|
35
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
36
|
+
event.preventDefault();
|
|
37
|
+
setCollapsed(!collapsed);
|
|
38
|
+
}
|
|
39
|
+
}}
|
|
40
|
+
>
|
|
41
|
+
{collapsed ? `${text.slice(0, COLLAPSED_TEXT_LENGTH)}...` : text}
|
|
42
|
+
</span>
|
|
43
|
+
);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/* oxlint-enable jsx-a11y/prefer-tag-over-role */
|
|
47
|
+
|
|
48
|
+
type LeafRenderer = (
|
|
49
|
+
data: string,
|
|
50
|
+
mimeType: OutputMessage["mimetype"],
|
|
51
|
+
) => React.ReactNode;
|
|
52
|
+
|
|
53
|
+
const LEAF_RENDERERS: Record<string, LeafRenderer> = {
|
|
54
|
+
"image/": (v) => <ImageOutput src={v} />,
|
|
55
|
+
"video/": (v) => <VideoOutput src={v} />,
|
|
56
|
+
"text/html:": (v) => (
|
|
57
|
+
<HtmlOutput html={v} inline={true} alwaysSanitizeHtml={false} />
|
|
58
|
+
),
|
|
59
|
+
"text/markdown:": (v) => (
|
|
60
|
+
<HtmlOutput html={v} inline={true} alwaysSanitizeHtml={true} />
|
|
61
|
+
),
|
|
62
|
+
"text/plain+float:": (v) => <span>{v}</span>,
|
|
63
|
+
"text/plain+bigint:": (v) => <span>{v}</span>,
|
|
64
|
+
"text/plain+set:": (v) => <span>{formatSetPayload(v)}</span>,
|
|
65
|
+
"text/plain+frozenset:": (v) => <span>{formatFrozensetPayload(v)}</span>,
|
|
66
|
+
"text/plain+tuple:": (v) => <span>{formatTuplePayload(v)}</span>,
|
|
67
|
+
"text/plain:": (v) => <CollapsibleTextOutput text={v} />,
|
|
68
|
+
"application/json:": (v) => <JsonOutput data={JSON.parse(v)} />,
|
|
69
|
+
"application/": (v, mime) => (
|
|
70
|
+
<OutputRenderer
|
|
71
|
+
message={{ channel: "output", data: v, mimetype: mime }}
|
|
72
|
+
renderFallback={() => (
|
|
73
|
+
<span>
|
|
74
|
+
{mime}:{v}
|
|
75
|
+
</span>
|
|
76
|
+
)}
|
|
77
|
+
/>
|
|
78
|
+
),
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
function tryRenderMimeLeaf(value: string): React.ReactNode | null {
|
|
82
|
+
for (const prefix of Object.keys(LEAF_RENDERERS)) {
|
|
83
|
+
if (value.startsWith(prefix)) {
|
|
84
|
+
const [data, mime] = splitLeaf(value);
|
|
85
|
+
return mime
|
|
86
|
+
? LEAF_RENDERERS[prefix](data, mime as OutputMessage["mimetype"])
|
|
87
|
+
: null;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
interface KeyRenderResult {
|
|
94
|
+
keyName?: string | number;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function renderKeyName(
|
|
98
|
+
props: Record<string, unknown>,
|
|
99
|
+
{ keyName }: KeyRenderResult,
|
|
100
|
+
): React.ReactNode {
|
|
101
|
+
const decoded = decodePythonKey(keyName);
|
|
102
|
+
if (!decoded) {
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
return <span {...props}>{decoded.text}</span>;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function renderKeyQuote(
|
|
109
|
+
_props: Record<string, unknown>,
|
|
110
|
+
{ keyName }: KeyRenderResult,
|
|
111
|
+
): React.ReactNode {
|
|
112
|
+
// The viewer treats null/undefined as a request for its default quotes.
|
|
113
|
+
return decodePythonKey(keyName)?.quoted === false ? <span /> : undefined;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export interface RenderResult {
|
|
117
|
+
type: string;
|
|
118
|
+
value?: unknown;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function renderStringValue(
|
|
122
|
+
_props: React.HTMLAttributes<HTMLSpanElement>,
|
|
123
|
+
result: RenderResult,
|
|
124
|
+
mode: "python" | "json",
|
|
125
|
+
): React.ReactNode {
|
|
126
|
+
if (result.type !== "value" || typeof result.value !== "string") {
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
const { value } = result;
|
|
130
|
+
if (mode !== "python") {
|
|
131
|
+
return renderPlainString(value);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const mimeResult = tryRenderMimeLeaf(value);
|
|
135
|
+
if (mimeResult !== null) {
|
|
136
|
+
return <span className="w-rjv-value">{mimeResult}</span>;
|
|
137
|
+
}
|
|
138
|
+
if (isUrl(value)) {
|
|
139
|
+
return (
|
|
140
|
+
<span className="w-rjv-value">
|
|
141
|
+
<a
|
|
142
|
+
href={value}
|
|
143
|
+
target="_blank"
|
|
144
|
+
rel="noopener noreferrer"
|
|
145
|
+
className="text-link hover:underline"
|
|
146
|
+
>
|
|
147
|
+
{value}
|
|
148
|
+
</a>
|
|
149
|
+
</span>
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
return renderPlainString(value);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function renderPlainString(value: string) {
|
|
156
|
+
if (value.length <= COLLAPSED_TEXT_LENGTH) {
|
|
157
|
+
return undefined;
|
|
158
|
+
}
|
|
159
|
+
return (
|
|
160
|
+
<span
|
|
161
|
+
className="inline-block"
|
|
162
|
+
style={{ color: "var(--w-rjv-type-string-color)" }}
|
|
163
|
+
>
|
|
164
|
+
"
|
|
165
|
+
<CollapsibleTextOutput text={value} />
|
|
166
|
+
"
|
|
167
|
+
</span>
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function renderArrow(
|
|
172
|
+
props: React.HTMLAttributes<HTMLSpanElement>,
|
|
173
|
+
{ keyName }: KeyRenderResult,
|
|
174
|
+
): React.ReactNode {
|
|
175
|
+
// alpha.43's data-expanded is inverted and omits initial collapse rules.
|
|
176
|
+
// NestedOpen's transform reflects the effective state.
|
|
177
|
+
const expanded = props.style?.transform === "rotate(0deg)";
|
|
178
|
+
return (
|
|
179
|
+
<button
|
|
180
|
+
{...props}
|
|
181
|
+
type="button"
|
|
182
|
+
aria-label={`${expanded ? "Collapse" : "Expand"} ${keyName ?? "root"}`}
|
|
183
|
+
aria-expanded={expanded}
|
|
184
|
+
/>
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function renderLiteral(text: string) {
|
|
189
|
+
return (_props: Record<string, unknown>, { type }: RenderResult) =>
|
|
190
|
+
type === "value" ? <span className="w-rjv-value">{text}</span> : undefined;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export const PYTHON_TRUE_RENDER = renderLiteral("True");
|
|
194
|
+
export const PYTHON_FALSE_RENDER = renderLiteral("False");
|
|
195
|
+
export const PYTHON_NULL_RENDER = renderLiteral("None");
|
|
196
|
+
export const JSON_NULL_RENDER = renderLiteral("null");
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
import type React from "react";
|
|
3
|
+
|
|
4
|
+
type ViewerStyle = React.CSSProperties & Record<`--w-rjv-${string}`, string>;
|
|
5
|
+
|
|
6
|
+
const BASE_THEME: ViewerStyle = {
|
|
7
|
+
fontSize: "var(--text-xs, .75rem)",
|
|
8
|
+
lineHeight: "var(--text-xs--line-height, 1rem)",
|
|
9
|
+
"--w-rjv-font-family": "monospace",
|
|
10
|
+
"--w-rjv-background-color": "transparent",
|
|
11
|
+
"--w-rjv-key-string": "var(--w-rjv-color)",
|
|
12
|
+
"--w-rjv-arrow-color": "var(--w-rjv-color)",
|
|
13
|
+
"--w-rjv-curlybraces-color": "var(--w-rjv-color)",
|
|
14
|
+
"--w-rjv-colon-color": "var(--w-rjv-color)",
|
|
15
|
+
"--w-rjv-brackets-color": "var(--w-rjv-color)",
|
|
16
|
+
"--w-rjv-quotes-color": "var(--w-rjv-color)",
|
|
17
|
+
"--w-rjv-ellipsis-color": "var(--w-rjv-type-string-color)",
|
|
18
|
+
"--w-rjv-quotes-string-color": "var(--w-rjv-type-string-color)",
|
|
19
|
+
"--w-rjv-type-bigint-color": "var(--w-rjv-type-int-color)",
|
|
20
|
+
"--w-rjv-type-url-color": "var(--link)",
|
|
21
|
+
"--w-rjv-type-nan-color": "var(--w-rjv-type-null-color)",
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const marimoLightTheme: ViewerStyle = {
|
|
25
|
+
...BASE_THEME,
|
|
26
|
+
"--w-rjv-color": "#002b36",
|
|
27
|
+
"--w-rjv-key-number": "#6c71c4",
|
|
28
|
+
"--w-rjv-line-color": "rgb(235, 235, 235)",
|
|
29
|
+
"--w-rjv-info-color": "rgba(0, 0, 0, 0.3)",
|
|
30
|
+
"--w-rjv-type-string-color": "#cb4b16",
|
|
31
|
+
"--w-rjv-type-int-color": "#268bd2",
|
|
32
|
+
"--w-rjv-type-float-color": "#859900",
|
|
33
|
+
"--w-rjv-type-boolean-color": "#2aa198",
|
|
34
|
+
"--w-rjv-type-date-color": "#586e75",
|
|
35
|
+
"--w-rjv-type-null-color": "#d33682",
|
|
36
|
+
"--w-rjv-type-undefined-color": "#586e75",
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const marimoDarkTheme: ViewerStyle = {
|
|
40
|
+
...BASE_THEME,
|
|
41
|
+
"--w-rjv-color": "#f8f8f8",
|
|
42
|
+
"--w-rjv-key-number": "#86c1b9",
|
|
43
|
+
"--w-rjv-line-color": "#383838",
|
|
44
|
+
"--w-rjv-info-color": "#b8b8b8",
|
|
45
|
+
"--w-rjv-type-string-color": "#dc9656",
|
|
46
|
+
"--w-rjv-type-int-color": "#a16946",
|
|
47
|
+
"--w-rjv-type-float-color": "#a1b56c",
|
|
48
|
+
"--w-rjv-type-boolean-color": "#ba8baf",
|
|
49
|
+
"--w-rjv-type-date-color": "#7cafc2",
|
|
50
|
+
"--w-rjv-type-null-color": "#ab4642",
|
|
51
|
+
"--w-rjv-type-undefined-color": "#d8d8d8",
|
|
52
|
+
};
|
package/src/hooks/useDebounce.ts
CHANGED
|
@@ -48,6 +48,8 @@ export function useDebounceControlledState<T>(opts: {
|
|
|
48
48
|
|
|
49
49
|
// Handle external changes:
|
|
50
50
|
useEffect(() => {
|
|
51
|
+
// TODO: Replace this synchronization with a controlled-state primitive.
|
|
52
|
+
// oxlint-disable-next-line react/no-deriving-state-in-effects -- Synchronize the debounced draft when the controlled value changes.
|
|
51
53
|
setInternalValue(initialValue);
|
|
52
54
|
}, [initialValue]);
|
|
53
55
|
|
|
@@ -9,21 +9,15 @@ const AccordionTrigger = AccordionPrimitive.Trigger;
|
|
|
9
9
|
|
|
10
10
|
import type { JSX } from "react";
|
|
11
11
|
import { ZodError } from "zod";
|
|
12
|
-
import { EmotionCacheProvider } from "@/components/editor/output/EmotionCacheProvider";
|
|
13
12
|
import { JsonOutput } from "@/components/editor/output/JsonOutput";
|
|
14
13
|
import { Alert, AlertTitle } from "../../components/ui/alert";
|
|
15
14
|
|
|
16
15
|
interface Props {
|
|
17
16
|
error: ZodError | Error;
|
|
18
|
-
shadowRoot: ShadowRoot | null;
|
|
19
17
|
badData: Record<string, unknown>;
|
|
20
18
|
}
|
|
21
19
|
|
|
22
|
-
export const BadPluginData: React.FC<Props> = ({
|
|
23
|
-
error,
|
|
24
|
-
badData,
|
|
25
|
-
shadowRoot,
|
|
26
|
-
}) => {
|
|
20
|
+
export const BadPluginData: React.FC<Props> = ({ error, badData }) => {
|
|
27
21
|
if (error instanceof ZodError) {
|
|
28
22
|
return (
|
|
29
23
|
<Alert variant="destructive">
|
|
@@ -49,9 +43,7 @@ export const BadPluginData: React.FC<Props> = ({
|
|
|
49
43
|
View Data:
|
|
50
44
|
</AccordionTrigger>
|
|
51
45
|
<AccordionContent className="text-[0.84375rem]">
|
|
52
|
-
<
|
|
53
|
-
<JsonOutput data={badData} />
|
|
54
|
-
</EmotionCacheProvider>
|
|
46
|
+
<JsonOutput data={badData} />
|
|
55
47
|
</AccordionContent>
|
|
56
48
|
</AccordionItem>
|
|
57
49
|
</Accordion>
|
|
@@ -65,9 +57,6 @@ export const BadPluginData: React.FC<Props> = ({
|
|
|
65
57
|
export function renderError(
|
|
66
58
|
error: ZodError | Error,
|
|
67
59
|
badData: Record<string, unknown>,
|
|
68
|
-
shadowRoot: ShadowRoot | null,
|
|
69
60
|
): JSX.Element {
|
|
70
|
-
return
|
|
71
|
-
<BadPluginData error={error} badData={badData} shadowRoot={shadowRoot} />
|
|
72
|
-
);
|
|
61
|
+
return <BadPluginData error={error} badData={badData} />;
|
|
73
62
|
}
|
|
@@ -219,6 +219,8 @@ const Form = ({
|
|
|
219
219
|
// The Form may be rendered before the child plugin is, so after mount
|
|
220
220
|
// we lookup the plugin once again.
|
|
221
221
|
useEffect(() => {
|
|
222
|
+
// TODO: Replace the post-mount registry lookup with reactive initialization.
|
|
223
|
+
// oxlint-disable-next-line react/no-deriving-state-in-effects -- The child registry entry may not exist until after the parent mounts.
|
|
222
224
|
setInternalValue(UI_ELEMENT_REGISTRY.lookupValue(elementId));
|
|
223
225
|
}, [elementId]);
|
|
224
226
|
|
|
@@ -126,6 +126,8 @@ const MatrixComponent = ({
|
|
|
126
126
|
// which avoids stale-state bugs when the matrix shape changes.
|
|
127
127
|
const [draft, setDraft] = useState(value);
|
|
128
128
|
useEffect(() => {
|
|
129
|
+
// TODO: Model the active draft without synchronizing state in an effect.
|
|
130
|
+
// oxlint-disable-next-line react/no-deriving-state-in-effects -- Synchronize the editable draft when the controlled value changes.
|
|
129
131
|
setDraft(value);
|
|
130
132
|
}, [value]);
|
|
131
133
|
const displayValue = activeCell == null ? value : draft;
|
|
@@ -90,6 +90,8 @@ const RangeSliderComponent = ({
|
|
|
90
90
|
|
|
91
91
|
// Update internal value on prop change
|
|
92
92
|
useEffect(() => {
|
|
93
|
+
// TODO: Replace this synchronization with a controlled-state primitive.
|
|
94
|
+
// oxlint-disable-next-line react/no-deriving-state-in-effects -- Synchronize the editable buffer when the controlled value changes.
|
|
93
95
|
setInternalValue(value);
|
|
94
96
|
}, [value]);
|
|
95
97
|
|
|
@@ -158,6 +158,8 @@ const SliderComponent = ({
|
|
|
158
158
|
const [internalValue, setInternalValue] = useState(value);
|
|
159
159
|
// Update internal value on prop change
|
|
160
160
|
useEffect(() => {
|
|
161
|
+
// TODO: Replace this synchronization with a controlled-state primitive.
|
|
162
|
+
// oxlint-disable-next-line react/no-deriving-state-in-effects -- Synchronize the editable buffer when the controlled value changes.
|
|
161
163
|
setInternalValue(value);
|
|
162
164
|
}, [value]);
|
|
163
165
|
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import type { JSX } from "react";
|
|
4
4
|
import { z } from "zod";
|
|
5
|
-
import { EmotionCacheProvider } from "../../components/editor/output/EmotionCacheProvider";
|
|
6
5
|
import { JsonOutput } from "../../components/editor/output/JsonOutput";
|
|
7
6
|
import type {
|
|
8
7
|
IStatelessPlugin,
|
|
@@ -30,18 +29,16 @@ export class JsonOutputPlugin implements IStatelessPlugin<Data> {
|
|
|
30
29
|
valueTypes: z.enum(["json", "python"]).default("python"),
|
|
31
30
|
});
|
|
32
31
|
|
|
33
|
-
render({ data
|
|
32
|
+
render({ data }: IStatelessPluginProps<Data>): JSX.Element {
|
|
34
33
|
// `false` defaults to no text label
|
|
35
34
|
const name = data.name === undefined ? false : data.name || "";
|
|
36
35
|
return (
|
|
37
|
-
<
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
/>
|
|
44
|
-
</EmotionCacheProvider>
|
|
36
|
+
<JsonOutput
|
|
37
|
+
data={data.jsonData}
|
|
38
|
+
format="auto"
|
|
39
|
+
valueTypes={data.valueTypes}
|
|
40
|
+
name={name}
|
|
41
|
+
/>
|
|
45
42
|
);
|
|
46
43
|
}
|
|
47
44
|
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
//#region ../node_modules/.pnpm/@emotion+memoize@0.9.0/node_modules/@emotion/memoize/dist/emotion-memoize.esm.js
|
|
2
|
-
function e(e) {
|
|
3
|
-
var t = Object.create(null);
|
|
4
|
-
return function(n) {
|
|
5
|
-
return t[n] === void 0 && (t[n] = e(n)), t[n];
|
|
6
|
-
};
|
|
7
|
-
}
|
|
8
|
-
//#endregion
|
|
9
|
-
//#region ../node_modules/.pnpm/@emotion+is-prop-valid@1.4.0/node_modules/@emotion/is-prop-valid/dist/emotion-is-prop-valid.esm.js
|
|
10
|
-
var t = /^((children|dangerouslySetInnerHTML|key|ref|autoFocus|defaultValue|defaultChecked|innerHTML|suppressContentEditableWarning|suppressHydrationWarning|valueLink|abbr|accept|acceptCharset|accessKey|action|allow|allowUserMedia|allowPaymentRequest|allowFullScreen|allowTransparency|alt|async|autoComplete|autoPlay|capture|cellPadding|cellSpacing|challenge|charSet|checked|cite|classID|className|cols|colSpan|content|contentEditable|contextMenu|controls|controlsList|coords|crossOrigin|data|dateTime|decoding|default|defer|dir|disabled|disablePictureInPicture|disableRemotePlayback|download|draggable|encType|enterKeyHint|fetchpriority|fetchPriority|form|formAction|formEncType|formMethod|formNoValidate|formTarget|frameBorder|headers|height|hidden|high|href|hrefLang|htmlFor|httpEquiv|id|inputMode|integrity|is|keyParams|keyType|kind|label|lang|list|loading|loop|low|marginHeight|marginWidth|max|maxLength|media|mediaGroup|method|min|minLength|multiple|muted|name|nonce|noValidate|open|optimum|pattern|placeholder|playsInline|popover|popoverTarget|popoverTargetAction|poster|preload|profile|radioGroup|readOnly|referrerPolicy|rel|required|reversed|role|rows|rowSpan|sandbox|scope|scoped|scrolling|seamless|selected|shape|size|sizes|slot|span|spellCheck|src|srcDoc|srcLang|srcSet|start|step|style|summary|tabIndex|target|title|translate|type|useMap|value|width|wmode|wrap|about|datatype|inlist|prefix|property|resource|typeof|vocab|autoCapitalize|autoCorrect|autoSave|color|incremental|fallback|inert|itemProp|itemScope|itemType|itemID|itemRef|on|option|results|security|unselectable|accentHeight|accumulate|additive|alignmentBaseline|allowReorder|alphabetic|amplitude|arabicForm|ascent|attributeName|attributeType|autoReverse|azimuth|baseFrequency|baselineShift|baseProfile|bbox|begin|bias|by|calcMode|capHeight|clip|clipPathUnits|clipPath|clipRule|colorInterpolation|colorInterpolationFilters|colorProfile|colorRendering|contentScriptType|contentStyleType|cursor|cx|cy|d|decelerate|descent|diffuseConstant|direction|display|divisor|dominantBaseline|dur|dx|dy|edgeMode|elevation|enableBackground|end|exponent|externalResourcesRequired|fill|fillOpacity|fillRule|filter|filterRes|filterUnits|floodColor|floodOpacity|focusable|fontFamily|fontSize|fontSizeAdjust|fontStretch|fontStyle|fontVariant|fontWeight|format|from|fr|fx|fy|g1|g2|glyphName|glyphOrientationHorizontal|glyphOrientationVertical|glyphRef|gradientTransform|gradientUnits|hanging|horizAdvX|horizOriginX|ideographic|imageRendering|in|in2|intercept|k|k1|k2|k3|k4|kernelMatrix|kernelUnitLength|kerning|keyPoints|keySplines|keyTimes|lengthAdjust|letterSpacing|lightingColor|limitingConeAngle|local|markerEnd|markerMid|markerStart|markerHeight|markerUnits|markerWidth|mask|maskContentUnits|maskUnits|mathematical|mode|numOctaves|offset|opacity|operator|order|orient|orientation|origin|overflow|overlinePosition|overlineThickness|panose1|paintOrder|pathLength|patternContentUnits|patternTransform|patternUnits|pointerEvents|points|pointsAtX|pointsAtY|pointsAtZ|preserveAlpha|preserveAspectRatio|primitiveUnits|r|radius|refX|refY|renderingIntent|repeatCount|repeatDur|requiredExtensions|requiredFeatures|restart|result|rotate|rx|ry|scale|seed|shapeRendering|slope|spacing|specularConstant|specularExponent|speed|spreadMethod|startOffset|stdDeviation|stemh|stemv|stitchTiles|stopColor|stopOpacity|strikethroughPosition|strikethroughThickness|string|stroke|strokeDasharray|strokeDashoffset|strokeLinecap|strokeLinejoin|strokeMiterlimit|strokeOpacity|strokeWidth|surfaceScale|systemLanguage|tableValues|targetX|targetY|textAnchor|textDecoration|textRendering|textLength|to|transform|u1|u2|underlinePosition|underlineThickness|unicode|unicodeBidi|unicodeRange|unitsPerEm|vAlphabetic|vHanging|vIdeographic|vMathematical|values|vectorEffect|version|vertAdvY|vertOriginX|vertOriginY|viewBox|viewTarget|visibility|widths|wordSpacing|writingMode|x|xHeight|x1|x2|xChannelSelector|xlinkActuate|xlinkArcrole|xlinkHref|xlinkRole|xlinkShow|xlinkTitle|xlinkType|xmlBase|xmlns|xmlnsXlink|xmlLang|xmlSpace|y|y1|y2|yChannelSelector|z|zoomAndPan|for|class|autofocus)|(([Dd][Aa][Tt][Aa]|[Aa][Rr][Ii][Aa]|x)-.*))$/, n = /* #__PURE__ */ e(function(e) {
|
|
11
|
-
return t.test(e) || e.charCodeAt(0) === 111 && e.charCodeAt(1) === 110 && e.charCodeAt(2) < 91;
|
|
12
|
-
});
|
|
13
|
-
//#endregion
|
|
14
|
-
export { e as n, n as t };
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
-
|
|
3
|
-
import createCache from "@emotion/cache";
|
|
4
|
-
import { CacheProvider } from "@emotion/react";
|
|
5
|
-
import { type PropsWithChildren, useMemo } from "react";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Custom Emotion cache provider that works in shadow roots.
|
|
9
|
-
* We don't directly use Emotion, but some of our dependencies do (via MUI).
|
|
10
|
-
*/
|
|
11
|
-
export const EmotionCacheProvider: React.FC<
|
|
12
|
-
PropsWithChildren<{ container: ShadowRoot | null }>
|
|
13
|
-
> = ({ container, children }) => {
|
|
14
|
-
const cache = useMemo(() => {
|
|
15
|
-
if (!container) {
|
|
16
|
-
return createCache({ key: "mo", prepend: true });
|
|
17
|
-
}
|
|
18
|
-
let emotionRoot = container.querySelector("style");
|
|
19
|
-
// Create a new style element if one doesn't exist
|
|
20
|
-
if (!emotionRoot) {
|
|
21
|
-
emotionRoot = document.createElement("style");
|
|
22
|
-
container.append(emotionRoot);
|
|
23
|
-
}
|
|
24
|
-
return createCache({
|
|
25
|
-
key: "mo",
|
|
26
|
-
prepend: true,
|
|
27
|
-
container: emotionRoot,
|
|
28
|
-
});
|
|
29
|
-
}, [container]);
|
|
30
|
-
|
|
31
|
-
return <CacheProvider value={cache}>{children}</CacheProvider>;
|
|
32
|
-
};
|