@marimo-team/islands 0.24.3-dev33 → 0.24.3-dev37

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.
Files changed (86) hide show
  1. package/dist/{ConnectedDataExplorerComponent-BLO7UDpo.js → ConnectedDataExplorerComponent-DeiKXaGB.js} +4 -4
  2. package/dist/{ErrorBoundary-B7uSdDUm.js → ErrorBoundary-B3IKS4qY.js} +1 -1
  3. package/dist/{_baseMerge-Bnytch8P.js → _baseMerge-BiGRwuqv.js} +1 -1
  4. package/dist/{abnfDiagram-VCTEODGH-DCb2K_RB.js → abnfDiagram-VCTEODGH-DKNGRyhn.js} +2 -2
  5. package/dist/{any-language-editor-C0dftc2Z.js → any-language-editor-BdvsE_1G.js} +2 -2
  6. package/dist/{architectureDiagram-5GKGNRK7-Bb_n642s.js → architectureDiagram-5GKGNRK7-C9oCEyV-.js} +1 -1
  7. package/dist/{chat-ui-DiMxe-GH.js → chat-ui-D15itIKJ.js} +8 -8
  8. package/dist/{chunk-IMKFNOWR-Du9BN2Ul.js → chunk-IMKFNOWR-ByL0a1rh.js} +1 -1
  9. package/dist/{chunk-SHT3W25Y-tUelricH.js → chunk-SHT3W25Y-D1Nb5X1c.js} +1 -1
  10. package/dist/{chunk-SVP7TREG-DK3egkfQ.js → chunk-SVP7TREG-BShKxcfD.js} +1 -1
  11. package/dist/{chunk-TICWLB2K-DRANTdnp.js → chunk-TICWLB2K-zpDbKPt8.js} +1 -1
  12. package/dist/{classDiagram-ZZMXUADV-7wCSVH7o.js → classDiagram-ZZMXUADV-qcsoIIQw.js} +2 -2
  13. package/dist/{classDiagram-v2-VYDZK3BY-QwnvcnhB.js → classDiagram-v2-VYDZK3BY-CAgMct5x.js} +2 -2
  14. package/dist/{common-0eAIfiA-.js → common-BsQjZnO0.js} +7921 -11807
  15. package/dist/{cynefinDiagram-5FMLGOSQ-CB7O4IZB.js → cynefinDiagram-5FMLGOSQ-BzBXigwN.js} +1 -1
  16. package/dist/{dagre-DfYd3btw.js → dagre-BGSfXNEb.js} +21 -21
  17. package/dist/{dagre-GXQ25YYZ-C-a95ISW.js → dagre-GXQ25YYZ-B-75J283.js} +2 -2
  18. package/dist/{data-grid-overlay-editor-C5eUZVTo.js → data-grid-overlay-editor-DMu5X3F1.js} +1 -1
  19. package/dist/{diagram-S7CK7UJ4-BsKLYQaH.js → diagram-S7CK7UJ4-Bzr5oWc0.js} +1 -1
  20. package/dist/{diagram-UQ7AKVKN-ppYdvuTN.js → diagram-UQ7AKVKN-CVIH_HJ_.js} +1 -1
  21. package/dist/{diagram-VX7I27RA-CAMK2l0E.js → diagram-VX7I27RA-BmoMwYLC.js} +1 -1
  22. package/dist/{diagram-Z3DM3KII-CxQ7MggT.js → diagram-Z3DM3KII-BW1w6mnj.js} +1 -1
  23. package/dist/{dropdown-menu-C73aTomr.js → dropdown-menu-WMrB1UnI.js} +1 -1
  24. package/dist/{ebnfDiagram-PWID7BFC-DzybELvk.js → ebnfDiagram-PWID7BFC-90Q6gkC0.js} +2 -2
  25. package/dist/{erDiagram-RLTQ6QDP-BVxV97ld.js → erDiagram-RLTQ6QDP-BZkLy3TR.js} +1 -1
  26. package/dist/{esm-blALgIVy.js → esm-qM9GvHZi.js} +293 -307
  27. package/dist/{flowDiagram-HODETNUW-BKvXsWYX.js → flowDiagram-HODETNUW-CD1F_wj5.js} +2 -2
  28. package/dist/{formats-vDAoRpYs.js → formats-B-gajYGs.js} +1 -1
  29. package/dist/{glide-data-editor-Bcy8iaZa.js → glide-data-editor-DzFEatOq.js} +2218 -2209
  30. package/dist/{glide-utils-BMnFm0vZ.js → glide-utils-4WmLlefP.js} +11 -11
  31. package/dist/{html-to-image-mVIbKn_Y.js → html-to-image-CZm_DMIE.js} +15 -15
  32. package/dist/{infoDiagram-27XIBGKW-B_ucqo-U.js → infoDiagram-27XIBGKW-t0zIIRVa.js} +1 -1
  33. package/dist/{ishikawaDiagram-5VMMS53U-BZFx9b-o.js → ishikawaDiagram-5VMMS53U-DtQUIYgx.js} +1 -1
  34. package/dist/{kanban-definition-UXKFOSKX-BpIfcu3G.js → kanban-definition-UXKFOSKX-CUMmwuNB.js} +1 -1
  35. package/dist/main.js +5274 -5283
  36. package/dist/{mermaid-B1Jb9HfY.js → mermaid-CMLxoJ0m.js} +34 -34
  37. package/dist/{mindmap-definition-YA3MSWOX-CmpAfv8K.js → mindmap-definition-YA3MSWOX-DnbkiQla.js} +1 -1
  38. package/dist/{number-overlay-editor-CFofZaOi.js → number-overlay-editor-lGqRHkFt.js} +1 -1
  39. package/dist/{extends-Do6pRuJa.js → objectWithoutPropertiesLoose-BbiTb8zy.js} +12 -1
  40. package/dist/{pegDiagram-XKGWAZYB-DO-0Q8SE.js → pegDiagram-XKGWAZYB-qgqAYMdv.js} +2 -2
  41. package/dist/{pick-B7vX8zxG.js → pick-CJeoxYOS.js} +1 -1
  42. package/dist/{pieDiagram-E7YTZNPT-B1MbuA_g.js → pieDiagram-E7YTZNPT-DJUB6w6B.js} +1 -1
  43. package/dist/{process-output-Cr6U5ULK.js → process-output-DuupNR0m.js} +1 -1
  44. package/dist/{railroadDiagram-O6MQD6OU-DPVOQUXC.js → railroadDiagram-O6MQD6OU-C0dtw4sR.js} +2 -2
  45. package/dist/{requirementDiagram-BXWQKSXE-CC7wZ-tM.js → requirementDiagram-BXWQKSXE-DqaTsUuC.js} +1 -1
  46. package/dist/{reveal-component-C6z2iWf2.js → reveal-component-baBJ3nFu.js} +486 -486
  47. package/dist/{sequenceDiagram-WJ2MYXX4-DomSyoJI.js → sequenceDiagram-WJ2MYXX4-D2NyWGuu.js} +1 -1
  48. package/dist/{spec-g740fOWu.js → spec-BiD5y7ft.js} +1 -1
  49. package/dist/{stateDiagram-D77RDMKH-SQLLjdtF.js → stateDiagram-D77RDMKH-JChw35bc.js} +3 -3
  50. package/dist/{stateDiagram-v2-MP3YSRHH-Dup4KkIr.js → stateDiagram-v2-MP3YSRHH-BfVY7OxA.js} +2 -2
  51. package/dist/style.css +1 -1
  52. package/dist/{swimlanes-42K2YHIH-ZXbhyGv6.js → swimlanes-42K2YHIH-eAB6utyY.js} +1 -1
  53. package/dist/{swimlanesDiagram-VR7AAH4N-CluwxV0g.js → swimlanesDiagram-VR7AAH4N-Cnpd2YtA.js} +2 -2
  54. package/dist/{timeline-definition-24CTP7MA-B8i6B9RR.js → timeline-definition-24CTP7MA-DlnODX3U.js} +1 -1
  55. package/dist/{toDate-C3JOXlJT.js → toDate-0cxDmdsp.js} +1 -1
  56. package/dist/{useAsyncData-C0f7qnnN.js → useAsyncData-DCIhDLCe.js} +1 -1
  57. package/dist/{useDeepCompareMemoize-QCa0HbWB.js → useDeepCompareMemoize-CLaZB7Gc.js} +1 -1
  58. package/dist/{useLifecycle-BGRBkTFT.js → useLifecycle-C1ViBACC.js} +1 -1
  59. package/dist/{useTheme-iQ0VoptU.js → useTheme-Da9LJvrQ.js} +1 -1
  60. package/dist/{vega-component-DRI4BDLQ.js → vega-component-Bt6d8B4Q.js} +5 -5
  61. package/dist/{vennDiagram-4TSXK5OY-CS7lHhxI.js → vennDiagram-4TSXK5OY-B_m5KxUg.js} +1 -1
  62. package/dist/{wardleyDiagram-VM6X3IG4-BSm-L8JI.js → wardleyDiagram-VM6X3IG4-Dw-Lgv3D.js} +1 -1
  63. package/dist/{xychartDiagram-S5SC5T6Z-8fKCJGrJ.js → xychartDiagram-S5SC5T6Z-BjfRms1e.js} +1 -1
  64. package/package.json +3 -6
  65. package/src/components/data-table/columns.tsx +64 -67
  66. package/src/components/editor/header/filename-input.tsx +2 -0
  67. package/src/components/editor/output/JsonOutput.tsx +182 -632
  68. package/src/components/editor/output/Outputs.css +49 -17
  69. package/src/components/editor/output/__tests__/JsonOutput-interactions.test.tsx +254 -0
  70. package/src/components/editor/output/__tests__/JsonOutput-mimetype.test.tsx +41 -46
  71. package/src/components/editor/output/__tests__/{json-output.test.ts → json-output-formatting.test.ts} +113 -75
  72. package/src/components/editor/output/__tests__/json-output-pagination.test.ts +252 -0
  73. package/src/components/editor/output/json-output/formatting.ts +167 -0
  74. package/src/components/editor/output/json-output/pagination.ts +155 -0
  75. package/src/components/editor/output/json-output/renderers.tsx +196 -0
  76. package/src/components/editor/output/json-output/themes.ts +52 -0
  77. package/src/hooks/useDebounce.ts +2 -0
  78. package/src/plugins/core/BadPlugin.tsx +3 -14
  79. package/src/plugins/core/registerReactComponent.tsx +0 -1
  80. package/src/plugins/impl/FormPlugin.tsx +2 -0
  81. package/src/plugins/impl/MatrixPlugin.tsx +2 -0
  82. package/src/plugins/impl/RangeSliderPlugin.tsx +2 -0
  83. package/src/plugins/impl/SliderPlugin.tsx +2 -0
  84. package/src/plugins/layout/JsonOutputPlugin.tsx +7 -10
  85. package/dist/emotion-is-prop-valid.esm-M57VsT8j.js +0 -14
  86. 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
+ &quot;
165
+ <CollapsibleTextOutput text={value} />
166
+ &quot;
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
+ };
@@ -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
- <EmotionCacheProvider container={shadowRoot}>
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
  }
@@ -274,7 +274,6 @@ function PluginSlotInternal<T>(
274
274
  Objects.mapValues(hostElement.dataset, (value) =>
275
275
  typeof value === "string" ? parseAttrValue(value) : value,
276
276
  ),
277
- hostElement.shadowRoot,
278
277
  );
279
278
  }
280
279
 
@@ -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, host }: IStatelessPluginProps<Data>): JSX.Element {
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
- <EmotionCacheProvider container={host.shadowRoot}>
38
- <JsonOutput
39
- data={data.jsonData}
40
- format="auto"
41
- valueTypes={data.valueTypes}
42
- name={name}
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
- };