@marimo-team/islands 0.18.2 → 0.18.4
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/{constants-DWBOe162.js → constants-D_G8vnDk.js} +5 -4
- package/dist/{formats-7RSCCoSI.js → formats-Bi_tbdwB.js} +21 -22
- package/dist/{glide-data-editor-D-Ia_Jsv.js → glide-data-editor-DXF8E-QD.js} +2 -2
- package/dist/main.js +280 -148
- package/dist/style.css +1 -1
- package/dist/{types-Dunk85GC.js → types-DclGb0Yh.js} +1 -1
- package/dist/{vega-component-kU4hFYYJ.js → vega-component-BFcH2SqR.js} +8 -8
- package/package.json +1 -1
- package/src/components/app-config/user-config-form.tsx +14 -1
- package/src/components/data-table/context-menu.tsx +7 -3
- package/src/components/data-table/filter-pills.tsx +2 -1
- package/src/components/data-table/filters.ts +11 -2
- package/src/components/editor/cell/CreateCellButton.tsx +5 -3
- package/src/components/editor/cell/collapse.tsx +2 -2
- package/src/components/editor/chrome/components/contribute-snippet-button.tsx +22 -103
- package/src/components/editor/controls/duplicate-shortcut-banner.tsx +50 -0
- package/src/components/editor/controls/keyboard-shortcuts.tsx +25 -2
- package/src/components/editor/notebook-banner.tsx +1 -1
- package/src/components/editor/notebook-cell.tsx +4 -3
- package/src/components/editor/output/__tests__/ansi-reduce.test.ts +6 -6
- package/src/components/editor/renderers/vertical-layout/vertical-layout.tsx +3 -3
- package/src/components/pages/home-page.tsx +6 -0
- package/src/components/scratchpad/scratchpad.tsx +2 -1
- package/src/core/constants.ts +10 -0
- package/src/core/layout/useTogglePresenting.ts +69 -25
- package/src/core/state/__mocks__/mocks.ts +1 -0
- package/src/hooks/__tests__/useDuplicateShortcuts.test.ts +449 -0
- package/src/hooks/useDuplicateShortcuts.ts +145 -0
- package/src/plugins/impl/NumberPlugin.tsx +1 -1
- package/src/plugins/impl/__tests__/NumberPlugin.test.tsx +1 -1
- package/src/plugins/impl/anywidget/AnyWidgetPlugin.tsx +67 -47
- package/src/plugins/impl/anywidget/__tests__/AnyWidgetPlugin.test.tsx +2 -57
- package/src/plugins/impl/anywidget/__tests__/model.test.ts +23 -19
- package/src/plugins/impl/anywidget/model.ts +68 -41
- package/src/plugins/impl/data-frames/utils/__tests__/operators.test.ts +2 -0
- package/src/plugins/impl/data-frames/utils/operators.ts +1 -0
- package/src/plugins/impl/vega/vega.css +5 -0
- package/src/plugins/layout/NavigationMenuPlugin.tsx +24 -22
- package/src/plugins/layout/StatPlugin.tsx +43 -23
- package/src/utils/__tests__/data-views.test.ts +495 -13
- package/src/utils/__tests__/json-parser.test.ts +1 -1
- package/src/utils/data-views.ts +134 -16
- package/src/utils/json/base64.ts +8 -0
package/src/utils/data-views.ts
CHANGED
|
@@ -1,19 +1,131 @@
|
|
|
1
1
|
/* Copyright 2024 Marimo. All rights reserved. */
|
|
2
|
-
import { set } from "lodash-es";
|
|
2
|
+
import { get, set } from "lodash-es";
|
|
3
3
|
import { invariant } from "./invariant";
|
|
4
|
+
import {
|
|
5
|
+
type Base64String,
|
|
6
|
+
binaryToByteString,
|
|
7
|
+
byteStringToBinary,
|
|
8
|
+
typedAtob,
|
|
9
|
+
typedBtoa,
|
|
10
|
+
} from "./json/base64";
|
|
4
11
|
import { Logger } from "./Logger";
|
|
5
12
|
|
|
6
13
|
/**
|
|
7
|
-
*
|
|
14
|
+
* Convert a DataView to a base64 string.
|
|
8
15
|
*/
|
|
9
|
-
export function
|
|
16
|
+
export function dataViewToBase64(dataView: DataView): Base64String {
|
|
17
|
+
const bytes = new Uint8Array(
|
|
18
|
+
dataView.buffer,
|
|
19
|
+
dataView.byteOffset,
|
|
20
|
+
dataView.byteLength,
|
|
21
|
+
);
|
|
22
|
+
const byteString = binaryToByteString(bytes);
|
|
23
|
+
return typedBtoa(byteString);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Recursively find all DataViews in an object and return their paths.
|
|
28
|
+
*
|
|
29
|
+
* This mirrors ipywidgets' _separate_buffers logic.
|
|
30
|
+
*/
|
|
31
|
+
function findDataViewPaths(
|
|
32
|
+
obj: unknown,
|
|
33
|
+
currentPath: (string | number)[] = [],
|
|
34
|
+
): (string | number)[][] {
|
|
35
|
+
const paths: (string | number)[][] = [];
|
|
36
|
+
|
|
37
|
+
if (obj instanceof DataView) {
|
|
38
|
+
paths.push(currentPath);
|
|
39
|
+
} else if (Array.isArray(obj)) {
|
|
40
|
+
for (let i = 0; i < obj.length; i++) {
|
|
41
|
+
paths.push(...findDataViewPaths(obj[i], [...currentPath, i]));
|
|
42
|
+
}
|
|
43
|
+
} else if (obj !== null && typeof obj === "object") {
|
|
44
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
45
|
+
paths.push(...findDataViewPaths(value, [...currentPath, key]));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return paths;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Serialize DataView buffers to base64 strings.
|
|
54
|
+
*
|
|
55
|
+
* Finds all DataViews in the object.
|
|
56
|
+
*/
|
|
57
|
+
export function serializeBuffersToBase64<T extends Record<string, unknown>>(
|
|
10
58
|
inputObject: T,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
59
|
+
): WireFormat<T> {
|
|
60
|
+
// Dynamically find all DataView paths instead of using fixed bufferPaths
|
|
61
|
+
const dataViewPaths = findDataViewPaths(inputObject);
|
|
62
|
+
|
|
63
|
+
if (dataViewPaths.length === 0) {
|
|
64
|
+
return { state: inputObject, buffers: [], bufferPaths: [] };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const state = structuredClone(inputObject);
|
|
68
|
+
const buffers: Base64String[] = [];
|
|
69
|
+
const bufferPaths: (string | number)[][] = [];
|
|
70
|
+
|
|
71
|
+
for (const bufferPath of dataViewPaths) {
|
|
72
|
+
const dataView = get(inputObject, bufferPath);
|
|
73
|
+
if (dataView instanceof DataView) {
|
|
74
|
+
const base64 = dataViewToBase64(dataView);
|
|
75
|
+
buffers.push(base64);
|
|
76
|
+
bufferPaths.push(bufferPath);
|
|
77
|
+
set(state, bufferPath, base64);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return { state, buffers, bufferPaths };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Wire format for anywidget state with binary data.
|
|
86
|
+
* Buffers can be either base64 strings (from network) or DataViews (in-memory).
|
|
87
|
+
*/
|
|
88
|
+
export interface WireFormat<T = Record<string, unknown>> {
|
|
89
|
+
state: T;
|
|
90
|
+
bufferPaths: (string | number)[][];
|
|
91
|
+
buffers: Base64String[];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Check if an object is in wire format.
|
|
96
|
+
*/
|
|
97
|
+
export function isWireFormat<T = Record<string, unknown>>(
|
|
98
|
+
obj: unknown,
|
|
99
|
+
): obj is WireFormat<T> {
|
|
100
|
+
return (
|
|
101
|
+
obj !== null &&
|
|
102
|
+
typeof obj === "object" &&
|
|
103
|
+
"state" in obj &&
|
|
104
|
+
"bufferPaths" in obj &&
|
|
105
|
+
"buffers" in obj
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Decode wire format or insert DataViews at specified paths.
|
|
111
|
+
*
|
|
112
|
+
* Accepts either:
|
|
113
|
+
* 1. Wire format: { state, bufferPaths, buffers } where buffers are base64 strings
|
|
114
|
+
* 2. Direct format: { state, bufferPaths, buffers } where buffers are DataViews
|
|
115
|
+
*
|
|
116
|
+
* For ndarray-like structures {view: null, dtype, shape}, we insert the DataView
|
|
117
|
+
* at the 'view' key, preserving the structure for round-tripping.
|
|
118
|
+
*/
|
|
119
|
+
export function decodeFromWire<T extends Record<string, unknown>>(input: {
|
|
120
|
+
state: T;
|
|
121
|
+
bufferPaths?: (string | number)[][];
|
|
122
|
+
buffers?: readonly (DataView | Base64String)[];
|
|
123
|
+
}): T {
|
|
124
|
+
const { state, bufferPaths, buffers } = input;
|
|
125
|
+
|
|
126
|
+
// If no buffer paths, return the original state
|
|
15
127
|
if (!bufferPaths || bufferPaths.length === 0) {
|
|
16
|
-
return
|
|
128
|
+
return state;
|
|
17
129
|
}
|
|
18
130
|
|
|
19
131
|
// If has buffers, assert they are the same size
|
|
@@ -24,18 +136,24 @@ export function updateBufferPaths<T extends Record<string, unknown>>(
|
|
|
24
136
|
);
|
|
25
137
|
}
|
|
26
138
|
|
|
27
|
-
|
|
139
|
+
const out = structuredClone(state);
|
|
28
140
|
|
|
29
141
|
for (const [i, bufferPath] of bufferPaths.entries()) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
Logger.warn("Could not find buffer at path", bufferPath);
|
|
142
|
+
const buffer = buffers?.[i];
|
|
143
|
+
|
|
144
|
+
if (buffer == null) {
|
|
145
|
+
Logger.warn("[anywidget] Could not find buffer at path", bufferPath);
|
|
35
146
|
continue;
|
|
36
147
|
}
|
|
37
|
-
|
|
148
|
+
|
|
149
|
+
// Handle both base64 strings (from wire format) and DataViews (direct usage)
|
|
150
|
+
if (typeof buffer === "string") {
|
|
151
|
+
const bytes = byteStringToBinary(typedAtob(buffer));
|
|
152
|
+
set(out, bufferPath, new DataView(bytes.buffer));
|
|
153
|
+
} else {
|
|
154
|
+
set(out, bufferPath, buffer);
|
|
155
|
+
}
|
|
38
156
|
}
|
|
39
157
|
|
|
40
|
-
return
|
|
158
|
+
return out;
|
|
41
159
|
}
|
package/src/utils/json/base64.ts
CHANGED
|
@@ -55,6 +55,14 @@ export function byteStringToBinary(bytes: ByteString): Uint8Array {
|
|
|
55
55
|
return Uint8Array.from(bytes, (c) => c.charCodeAt(0));
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
export function binaryToByteString(binary: Uint8Array): ByteString {
|
|
59
|
+
let result = "";
|
|
60
|
+
for (const byte of binary) {
|
|
61
|
+
result += String.fromCharCode(byte);
|
|
62
|
+
}
|
|
63
|
+
return result as ByteString;
|
|
64
|
+
}
|
|
65
|
+
|
|
58
66
|
export function safeExtractSetUIElementMessageBuffers(
|
|
59
67
|
op: OperationMessageData<"send-ui-element-message">,
|
|
60
68
|
): readonly DataView[] {
|