@marimo-team/islands 0.19.7-dev43 → 0.19.7-dev46
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/main.js +19 -12
- package/package.json +2 -2
- package/src/core/ai/tools/__tests__/edit-notebook-tool.test.ts +1 -0
- package/src/core/codemirror/cm.ts +1 -0
- package/src/core/codemirror/config/extension.ts +3 -0
- package/src/core/codemirror/copilot/__tests__/getCodes.test.ts +1 -0
- package/src/core/codemirror/facet.ts +4 -1
- package/src/core/codemirror/language/__tests__/extension.test.ts +1 -0
- package/src/core/codemirror/language/__tests__/utils.test.ts +1 -0
- package/src/core/codemirror/language/extension.ts +12 -0
- package/src/core/codemirror/language/languages/python.ts +1 -1
- package/src/core/codemirror/lsp/notebook-lsp.ts +36 -16
- package/src/utils/__tests__/iframe.test.ts +9 -14
- package/src/utils/download.ts +6 -4
- package/src/utils/iframe.ts +3 -10
package/dist/main.js
CHANGED
|
@@ -17762,7 +17762,7 @@ ${JSON.stringify(e2, null, 4)}`);
|
|
|
17762
17762
|
label: f,
|
|
17763
17763
|
detail: (d == null ? void 0 : d.detail) || c,
|
|
17764
17764
|
apply(e2, c2, d2, _2) {
|
|
17765
|
-
if (v && isLSPTextEdit(v) ? e2.dispatch(insertCompletionText(e2.state, v.newText, posToOffsetOrZero(e2.state.doc, v.range.start), posToOffsetOrZero(e2.state.doc, v.range.end))) : y && E === InsertTextFormat.Snippet && r.useSnippetOnCompletion ? snippet(convertSnippet(y))(e2, null, d2, _2) : e2.dispatch(insertCompletionText(e2.state, f, d2, _2)), !w) return;
|
|
17765
|
+
if (v && isLSPTextEdit(v) ? e2.dispatch(insertCompletionText(e2.state, v.newText, posToOffsetOrZero(e2.state.doc, v.range.start), posToOffsetOrZero(e2.state.doc, v.range.end))) : y && E === InsertTextFormat.Snippet && r.useSnippetOnCompletion ? snippet(convertSnippet(y))(e2, null, d2, _2) : e2.dispatch(insertCompletionText(e2.state, y || f, d2, _2)), !w) return;
|
|
17766
17766
|
let S2 = w.sort(({ range: { end: r2 } }, { range: { end: c3 } }) => posToOffsetOrZero(e2.state.doc, r2) < posToOffsetOrZero(e2.state.doc, c3) ? 1 : posToOffsetOrZero(e2.state.doc, r2) > posToOffsetOrZero(e2.state.doc, c3) ? -1 : 0);
|
|
17767
17767
|
for (let r2 of S2) e2.dispatch(e2.state.update({
|
|
17768
17768
|
changes: {
|
|
@@ -18703,7 +18703,7 @@ ${JSON.stringify(e2, null, 4)}`);
|
|
|
18703
18703
|
}
|
|
18704
18704
|
function singleFacet() {
|
|
18705
18705
|
return Facet$1.define({
|
|
18706
|
-
combine: (e) => e
|
|
18706
|
+
combine: (e) => e.find((e2) => e2 !== void 0)
|
|
18707
18707
|
});
|
|
18708
18708
|
}
|
|
18709
18709
|
singleFacet(), singleFacet(), singleFacet();
|
|
@@ -32350,7 +32350,6 @@ ${c.sqlString}
|
|
|
32350
32350
|
var Snapshotter = class {
|
|
32351
32351
|
constructor(e) {
|
|
32352
32352
|
__publicField(this, "documentVersion", 0);
|
|
32353
|
-
__publicField(this, "versionToCellNumberAndVersion", new LRUCache(20));
|
|
32354
32353
|
__publicField(this, "lastSnapshot", null);
|
|
32355
32354
|
this.getNotebookCode = e;
|
|
32356
32355
|
}
|
|
@@ -32367,11 +32366,6 @@ ${c.sqlString}
|
|
|
32367
32366
|
version: this.documentVersion
|
|
32368
32367
|
};
|
|
32369
32368
|
}
|
|
32370
|
-
getSnapshot(e) {
|
|
32371
|
-
let r = this.versionToCellNumberAndVersion.get(e);
|
|
32372
|
-
if (!r) throw Error(`No snapshot for version ${e}`);
|
|
32373
|
-
return r;
|
|
32374
|
-
}
|
|
32375
32369
|
getLatestSnapshot() {
|
|
32376
32370
|
if (!this.lastSnapshot) throw Error("No snapshots");
|
|
32377
32371
|
return {
|
|
@@ -32611,13 +32605,26 @@ ${c.sqlString}
|
|
|
32611
32605
|
}), d.contents === "" ? null : (d.range && (d.range = r.reverseRange(d.range, c)), d)) : (Logger.debug("[lsp] no hover result"), d);
|
|
32612
32606
|
}
|
|
32613
32607
|
async textDocumentCompletion(e) {
|
|
32614
|
-
let { lens: r } = this.snapshotter.getLatestSnapshot()
|
|
32608
|
+
let { lens: r } = this.snapshotter.getLatestSnapshot();
|
|
32609
|
+
if (!CellDocumentUri.is(e.textDocument.uri)) return Logger.error("[lsp] Invalid cell document URI in completion request", e.textDocument.uri), null;
|
|
32610
|
+
let c = CellDocumentUri.parse(e.textDocument.uri);
|
|
32611
|
+
if (!c || c === "undefined") return Logger.error("[lsp] Invalid cellId 'undefined' in completion request", {
|
|
32612
|
+
cellId: c,
|
|
32613
|
+
uri: e.textDocument.uri,
|
|
32614
|
+
availableCellIds: r.cellIds
|
|
32615
|
+
}), null;
|
|
32616
|
+
r.cellIds.includes(c) || Logger.warn("[lsp] CellId in completion request not found in current lens", {
|
|
32617
|
+
cellId: c,
|
|
32618
|
+
uri: e.textDocument.uri,
|
|
32619
|
+
availableCellIds: r.cellIds
|
|
32620
|
+
});
|
|
32621
|
+
let d = r.transformPosition(e.position, c);
|
|
32615
32622
|
return this.client.textDocumentCompletion({
|
|
32616
32623
|
...e,
|
|
32617
32624
|
textDocument: {
|
|
32618
32625
|
uri: this.documentUri
|
|
32619
32626
|
},
|
|
32620
|
-
position:
|
|
32627
|
+
position: d
|
|
32621
32628
|
});
|
|
32622
32629
|
}
|
|
32623
32630
|
patchProcessNotification() {
|
|
@@ -32944,7 +32951,7 @@ ${c.sqlString}
|
|
|
32944
32951
|
client: r2,
|
|
32945
32952
|
languageId: "python",
|
|
32946
32953
|
allowHTMLContent: true,
|
|
32947
|
-
useSnippetOnCompletion:
|
|
32954
|
+
useSnippetOnCompletion: true,
|
|
32948
32955
|
hoverConfig: _,
|
|
32949
32956
|
completionConfig: d2,
|
|
32950
32957
|
diagnosticsEnabled: ((_d2 = f.diagnostics) == null ? void 0 : _d2.enabled) ?? false,
|
|
@@ -73168,7 +73175,7 @@ Image URL: ${r.imageUrl}`)), contextToXml({
|
|
|
73168
73175
|
return Logger.warn("Failed to get version from mount config"), null;
|
|
73169
73176
|
}
|
|
73170
73177
|
}
|
|
73171
|
-
const marimoVersionAtom = atom(getVersionFromMountConfig() || "0.19.7-
|
|
73178
|
+
const marimoVersionAtom = atom(getVersionFromMountConfig() || "0.19.7-dev46"), showCodeInRunModeAtom = atom(true);
|
|
73172
73179
|
atom(null);
|
|
73173
73180
|
var import_compiler_runtime$88 = require_compiler_runtime();
|
|
73174
73181
|
function useKeydownOnElement(e, r) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@marimo-team/islands",
|
|
3
|
-
"version": "0.19.7-
|
|
3
|
+
"version": "0.19.7-dev46",
|
|
4
4
|
"main": "dist/main.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"@lezer/markdown": "^1.6.2",
|
|
56
56
|
"@lezer/python": "^1.1.18",
|
|
57
57
|
"@marimo-team/codemirror-ai": "^0.3.5",
|
|
58
|
-
"@marimo-team/codemirror-languageserver": "^1.16.
|
|
58
|
+
"@marimo-team/codemirror-languageserver": "^1.16.11",
|
|
59
59
|
"@marimo-team/codemirror-mcp": "^0.1.5",
|
|
60
60
|
"@marimo-team/codemirror-sql": "^0.2.4",
|
|
61
61
|
"@marimo-team/llm-info": "workspace:*",
|
|
@@ -41,12 +41,14 @@ export const lspConfigState = singleFacet<
|
|
|
41
41
|
* Extension for cell config
|
|
42
42
|
*/
|
|
43
43
|
export function cellConfigExtension({
|
|
44
|
+
cellId,
|
|
44
45
|
completionConfig,
|
|
45
46
|
hotkeys,
|
|
46
47
|
placeholderType,
|
|
47
48
|
lspConfig,
|
|
48
49
|
diagnosticsConfig,
|
|
49
50
|
}: {
|
|
51
|
+
cellId: CellId;
|
|
50
52
|
completionConfig: CompletionConfig;
|
|
51
53
|
hotkeys: HotkeyProvider;
|
|
52
54
|
placeholderType: PlaceholderType;
|
|
@@ -55,6 +57,7 @@ export function cellConfigExtension({
|
|
|
55
57
|
}) {
|
|
56
58
|
return [
|
|
57
59
|
// Store state
|
|
60
|
+
cellIdState.of(cellId),
|
|
58
61
|
completionConfigState.of(completionConfig),
|
|
59
62
|
hotkeysProviderState.of(hotkeys),
|
|
60
63
|
placeholderState.of(placeholderType),
|
|
@@ -14,6 +14,9 @@ import { Facet } from "@codemirror/state";
|
|
|
14
14
|
*/
|
|
15
15
|
export function singleFacet<T>() {
|
|
16
16
|
return Facet.define<T, T>({
|
|
17
|
-
combine: (values) =>
|
|
17
|
+
combine: (values) => {
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
19
|
+
return values.find((v) => v !== undefined)!;
|
|
20
|
+
},
|
|
18
21
|
});
|
|
19
22
|
}
|
|
@@ -15,6 +15,7 @@ import type {
|
|
|
15
15
|
LSPConfig,
|
|
16
16
|
} from "@/core/config/config-schema";
|
|
17
17
|
import type { HotkeyProvider } from "@/core/hotkeys/hotkeys";
|
|
18
|
+
import { Logger } from "@/utils/Logger";
|
|
18
19
|
import { clamp } from "@/utils/math";
|
|
19
20
|
import {
|
|
20
21
|
cellIdState,
|
|
@@ -310,6 +311,17 @@ export function reconfigureLanguageEffect(
|
|
|
310
311
|
const language = view.state.field(languageAdapterState);
|
|
311
312
|
const placeholderType = view.state.facet(placeholderState);
|
|
312
313
|
const cellId = view.state.facet(cellIdState);
|
|
314
|
+
|
|
315
|
+
if (cellId === undefined) {
|
|
316
|
+
Logger.error("Cell ID is undefined in reconfigureLanguageEffect");
|
|
317
|
+
}
|
|
318
|
+
if (placeholderType === undefined) {
|
|
319
|
+
Logger.error("Placeholder type is undefined in reconfigureLanguageEffect");
|
|
320
|
+
}
|
|
321
|
+
if (completionConfig === undefined) {
|
|
322
|
+
Logger.error("Completion config is undefined in reconfigureLanguageEffect");
|
|
323
|
+
}
|
|
324
|
+
|
|
313
325
|
return languageCompartment.reconfigure(
|
|
314
326
|
language.getExtension(
|
|
315
327
|
cellId,
|
|
@@ -293,7 +293,7 @@ export class PythonLanguageAdapter implements LanguageAdapter<{}> {
|
|
|
293
293
|
client: client as unknown as LanguageServerClient,
|
|
294
294
|
languageId: "python",
|
|
295
295
|
allowHTMLContent: true,
|
|
296
|
-
useSnippetOnCompletion:
|
|
296
|
+
useSnippetOnCompletion: true,
|
|
297
297
|
hoverConfig: hoverOptions,
|
|
298
298
|
completionConfig: autocompleteOptions,
|
|
299
299
|
// Default to false
|
|
@@ -47,13 +47,6 @@ class Snapshotter {
|
|
|
47
47
|
this.getNotebookCode = getNotebookCode;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
/**
|
|
51
|
-
* Map from the global document version to the cell id and version.
|
|
52
|
-
*/
|
|
53
|
-
private versionToCellNumberAndVersion = new LRUCache<number, NotebookLens>(
|
|
54
|
-
20,
|
|
55
|
-
);
|
|
56
|
-
|
|
57
50
|
private lastSnapshot: NotebookLens | null = null;
|
|
58
51
|
|
|
59
52
|
public snapshot() {
|
|
@@ -78,14 +71,6 @@ class Snapshotter {
|
|
|
78
71
|
};
|
|
79
72
|
}
|
|
80
73
|
|
|
81
|
-
public getSnapshot(version: number) {
|
|
82
|
-
const snapshot = this.versionToCellNumberAndVersion.get(version);
|
|
83
|
-
if (!snapshot) {
|
|
84
|
-
throw new Error(`No snapshot for version ${version}`);
|
|
85
|
-
}
|
|
86
|
-
return snapshot;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
74
|
public getLatestSnapshot() {
|
|
90
75
|
if (!this.lastSnapshot) {
|
|
91
76
|
throw new Error("No snapshots");
|
|
@@ -657,14 +642,49 @@ export class NotebookLanguageServerClient implements ILanguageServerClient {
|
|
|
657
642
|
|
|
658
643
|
public async textDocumentCompletion(params: LSP.CompletionParams) {
|
|
659
644
|
const { lens } = this.snapshotter.getLatestSnapshot();
|
|
645
|
+
|
|
646
|
+
// Check if URI is valid
|
|
647
|
+
if (!CellDocumentUri.is(params.textDocument.uri)) {
|
|
648
|
+
Logger.error(
|
|
649
|
+
"[lsp] Invalid cell document URI in completion request",
|
|
650
|
+
params.textDocument.uri,
|
|
651
|
+
);
|
|
652
|
+
return null;
|
|
653
|
+
}
|
|
654
|
+
|
|
660
655
|
const cellId = CellDocumentUri.parse(params.textDocument.uri);
|
|
661
656
|
|
|
657
|
+
// Check if cellId is valid (not undefined string)
|
|
658
|
+
if (!cellId || cellId === "undefined") {
|
|
659
|
+
Logger.error("[lsp] Invalid cellId 'undefined' in completion request", {
|
|
660
|
+
cellId,
|
|
661
|
+
uri: params.textDocument.uri,
|
|
662
|
+
availableCellIds: lens.cellIds,
|
|
663
|
+
});
|
|
664
|
+
// Return null to fail gracefully instead of sending wrong position
|
|
665
|
+
return null;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
// Warn if cellId not found in lens (might be okay if cell was just added)
|
|
669
|
+
if (!lens.cellIds.includes(cellId)) {
|
|
670
|
+
Logger.warn(
|
|
671
|
+
"[lsp] CellId in completion request not found in current lens",
|
|
672
|
+
{
|
|
673
|
+
cellId,
|
|
674
|
+
uri: params.textDocument.uri,
|
|
675
|
+
availableCellIds: lens.cellIds,
|
|
676
|
+
},
|
|
677
|
+
);
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
const transformedPosition = lens.transformPosition(params.position, cellId);
|
|
681
|
+
|
|
662
682
|
return this.client.textDocumentCompletion({
|
|
663
683
|
...params,
|
|
664
684
|
textDocument: {
|
|
665
685
|
uri: this.documentUri,
|
|
666
686
|
},
|
|
667
|
-
position:
|
|
687
|
+
position: transformedPosition,
|
|
668
688
|
});
|
|
669
689
|
}
|
|
670
690
|
|
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
|
-
import {
|
|
3
|
+
import { captureExternalIframes } from "../iframe";
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
vi.mock("html-to-image", () => ({
|
|
7
|
-
toPng: vi.fn().mockResolvedValue("data:image/png;base64,mock"),
|
|
8
|
-
}));
|
|
9
|
-
|
|
10
|
-
describe("captureIframeAsImage", () => {
|
|
5
|
+
describe("captureExternalIframes", () => {
|
|
11
6
|
const originalCreateElement = document.createElement.bind(document);
|
|
12
7
|
|
|
13
8
|
beforeEach(() => {
|
|
@@ -53,7 +48,7 @@ describe("captureIframeAsImage", () => {
|
|
|
53
48
|
const element = document.createElement("div");
|
|
54
49
|
element.innerHTML = "<p>No iframe here</p>";
|
|
55
50
|
|
|
56
|
-
const result = await
|
|
51
|
+
const result = await captureExternalIframes(element);
|
|
57
52
|
expect(result).toBeNull();
|
|
58
53
|
});
|
|
59
54
|
|
|
@@ -61,7 +56,7 @@ describe("captureIframeAsImage", () => {
|
|
|
61
56
|
const element = document.createElement("div");
|
|
62
57
|
element.innerHTML = '<iframe src="https://external.com/page"></iframe>';
|
|
63
58
|
|
|
64
|
-
const result = await
|
|
59
|
+
const result = await captureExternalIframes(element);
|
|
65
60
|
|
|
66
61
|
expect(result).not.toBeNull();
|
|
67
62
|
expect(result).toMatch(/^data:image\/png;base64,/);
|
|
@@ -72,7 +67,7 @@ describe("captureIframeAsImage", () => {
|
|
|
72
67
|
element.innerHTML =
|
|
73
68
|
'<iframe src="https://www.openstreetmap.org/export/embed.html"></iframe>';
|
|
74
69
|
|
|
75
|
-
const result = await
|
|
70
|
+
const result = await captureExternalIframes(element);
|
|
76
71
|
|
|
77
72
|
expect(result).not.toBeNull();
|
|
78
73
|
expect(result).toMatch(/^data:image\/png;base64,/);
|
|
@@ -85,7 +80,7 @@ describe("captureIframeAsImage", () => {
|
|
|
85
80
|
element.append(iframe);
|
|
86
81
|
|
|
87
82
|
// The iframe has no body accessible in jsdom
|
|
88
|
-
const result = await
|
|
83
|
+
const result = await captureExternalIframes(element);
|
|
89
84
|
|
|
90
85
|
// In jsdom, contentDocument may not be accessible
|
|
91
86
|
expect(result).toBeNull();
|
|
@@ -96,7 +91,7 @@ describe("captureIframeAsImage", () => {
|
|
|
96
91
|
element.innerHTML = '<iframe src="./@file/123.html"></iframe>';
|
|
97
92
|
|
|
98
93
|
// Same-origin relative URL, but contentDocument not accessible in jsdom
|
|
99
|
-
const result = await
|
|
94
|
+
const result = await captureExternalIframes(element);
|
|
100
95
|
|
|
101
96
|
// Returns null because jsdom can't access contentDocument for file URLs
|
|
102
97
|
expect(result).toBeNull();
|
|
@@ -113,7 +108,7 @@ describe("captureIframeAsImage", () => {
|
|
|
113
108
|
const element = document.createElement("div");
|
|
114
109
|
element.innerHTML = `<iframe src="${url}"></iframe>`;
|
|
115
110
|
|
|
116
|
-
const result = await
|
|
111
|
+
const result = await captureExternalIframes(element);
|
|
117
112
|
|
|
118
113
|
expect(result).not.toBeNull();
|
|
119
114
|
expect(result).toMatch(/^data:image\/png;base64,/);
|
|
@@ -128,7 +123,7 @@ describe("captureIframeAsImage", () => {
|
|
|
128
123
|
const element = document.createElement("div");
|
|
129
124
|
element.innerHTML = `<iframe src="${url}"></iframe>`;
|
|
130
125
|
|
|
131
|
-
const result = await
|
|
126
|
+
const result = await captureExternalIframes(element);
|
|
132
127
|
|
|
133
128
|
// In jsdom, these return null because contentDocument isn't accessible
|
|
134
129
|
// but they should NOT return a placeholder (which would indicate external detection)
|
package/src/utils/download.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { Filenames } from "@/utils/filenames";
|
|
|
8
8
|
import { Paths } from "@/utils/paths";
|
|
9
9
|
import { prettyError } from "./errors";
|
|
10
10
|
import { toPng } from "./html-to-image";
|
|
11
|
-
import {
|
|
11
|
+
import { captureExternalIframes } from "./iframe";
|
|
12
12
|
import { Logger } from "./Logger";
|
|
13
13
|
import { ProgressState } from "./progress";
|
|
14
14
|
import { ToastProgress } from "./toast-progress";
|
|
@@ -66,9 +66,11 @@ export async function getImageDataUrlForCell(
|
|
|
66
66
|
return;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
// TODO: This doesn't handle external iframes + normal elements together (eg. in vstack).
|
|
70
|
+
// It will return the iframe only
|
|
71
|
+
const externalIframeDataUrl = await captureExternalIframes(element);
|
|
72
|
+
if (externalIframeDataUrl) {
|
|
73
|
+
return externalIframeDataUrl;
|
|
72
74
|
}
|
|
73
75
|
|
|
74
76
|
const startTime = Date.now();
|
package/src/utils/iframe.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
|
-
import { toPng } from "./html-to-image";
|
|
4
|
-
|
|
5
3
|
const PLACEHOLDER_WIDTH = 320;
|
|
6
4
|
const PLACEHOLDER_HEIGHT = 180;
|
|
7
5
|
|
|
@@ -95,11 +93,11 @@ function createPlaceholderImage(url: string | null): string {
|
|
|
95
93
|
}
|
|
96
94
|
|
|
97
95
|
/**
|
|
98
|
-
* Capture
|
|
96
|
+
* Capture external iframes as a PNG image. External iframes are not supported by html-to-image.
|
|
99
97
|
* @param element - The element to capture the iframe from
|
|
100
98
|
* @returns The image data URL of the iframe, or a placeholder image if the iframe is external
|
|
101
99
|
*/
|
|
102
|
-
export async function
|
|
100
|
+
export async function captureExternalIframes(
|
|
103
101
|
element: HTMLElement,
|
|
104
102
|
): Promise<string | null> {
|
|
105
103
|
const iframe = element.querySelector("iframe");
|
|
@@ -133,10 +131,5 @@ export async function captureIframeAsImage(
|
|
|
133
131
|
}
|
|
134
132
|
}
|
|
135
133
|
|
|
136
|
-
|
|
137
|
-
try {
|
|
138
|
-
return await toPng(doc.body);
|
|
139
|
-
} catch {
|
|
140
|
-
return createPlaceholderImage(null);
|
|
141
|
-
}
|
|
134
|
+
return null;
|
|
142
135
|
}
|