@marimo-team/frontend 0.23.16-dev35 → 0.23.16-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.
Files changed (105) hide show
  1. package/dist/assets/{CellStatus-CmojVYzm.js → CellStatus-Dua-Q5qp.js} +1 -1
  2. package/dist/assets/{JsonOutput-BwwRpByA.js → JsonOutput-BjTsPSAI.js} +12 -12
  3. package/dist/assets/{MarimoErrorOutput-BWte8vjk.js → MarimoErrorOutput-BSyqD6Hg.js} +8 -8
  4. package/dist/assets/{RenderHTML-DRltV71H.js → RenderHTML-DG0Fm01X.js} +1 -1
  5. package/dist/assets/{RunButton-DIegd2mN.js → RunButton-DFs1N-c9.js} +1 -1
  6. package/dist/assets/{add-cell-with-ai-BBFD-M9V.js → add-cell-with-ai-C6Kxfu_D.js} +17 -17
  7. package/dist/assets/{add-connection-dialog-CECJ8507.js → add-connection-dialog-DAqIrB2O.js} +1 -1
  8. package/dist/assets/{agent-panel-YaS0WXP1.js → agent-panel-GtkUm7qc.js} +5 -5
  9. package/dist/assets/ai-model-dropdown-UEHQ_Tx0.js +5 -0
  10. package/dist/assets/{app-config-button-BW-w01aF.js → app-config-button-BmBlROBo.js} +1 -1
  11. package/dist/assets/{cell-editor-EV-1IccQ.js → cell-editor-DKzBZjFa.js} +10 -10
  12. package/dist/assets/{cell-link-C5gpAn3R.js → cell-link-CRHwRCw2.js} +1 -1
  13. package/dist/assets/cells-C-9Qa_6_.js +235 -0
  14. package/dist/assets/{chat-display-8BbC5B-D.js → chat-display-CqB9H1kF.js} +1 -1
  15. package/dist/assets/{chat-panel-CMOZp5xW.js → chat-panel-C3wpuHtk.js} +2 -2
  16. package/dist/assets/{chat-ui-DWhJ2Ozy.js → chat-ui-DF2nZlBy.js} +1 -1
  17. package/dist/assets/{column-preview-DeXESYpM.js → column-preview-Bp4QHPE2.js} +1 -1
  18. package/dist/assets/{command-palette-Dx6AkiPd.js → command-palette-DaJdKaQz.js} +1 -1
  19. package/dist/assets/{common-XVELED_O.js → common-DeheUlBO.js} +1 -1
  20. package/dist/assets/{components-BLHpmhXx.js → components-Dk-7Tn_8.js} +1 -1
  21. package/dist/assets/{datasource-CTqa8bf-.js → datasource-D-oZZRFN.js} +2 -2
  22. package/dist/assets/{dependency-graph-panel-C4Nzcddp.js → dependency-graph-panel-JK4qjNID.js} +3 -3
  23. package/dist/assets/{documentation-panel-Bym4G7IS.js → documentation-panel-C_TxLeX8.js} +1 -1
  24. package/dist/assets/{download-HKgqJX4l.js → download-QhwPYWYu.js} +2 -2
  25. package/dist/assets/{edit-page-Jtp6Qqjl.js → edit-page-C8yy6X_7.js} +6 -6
  26. package/dist/assets/{error-entries-CxyvaxEM.js → error-entries-B8hPG61B.js} +2 -2
  27. package/dist/assets/{error-panel-CfSy7uUr.js → error-panel-BOBOm9wN.js} +1 -1
  28. package/dist/assets/{file-explorer-panel-Dd5Xn2TK.js → file-explorer-panel-CQOFpuIG.js} +4 -4
  29. package/dist/assets/{file-icons-CSjJ3JC6.js → file-icons-DoBeqg86.js} +1 -1
  30. package/dist/assets/{file-name-input-CrKtrXMl.js → file-name-input-B8d-QPzY.js} +1 -1
  31. package/dist/assets/{floating-outline-ByQ8vvif.js → floating-outline-ylNOBTvA.js} +1 -1
  32. package/dist/assets/{focus-CVONl-Dy.js → focus-CJzSyxLL.js} +1 -1
  33. package/dist/assets/{form-Ca3yd_Od.js → form-C2_p_FeE.js} +2 -2
  34. package/dist/assets/{home-page-FpAawj9s.js → home-page-D_6eKuGc.js} +2 -2
  35. package/dist/assets/{html-to-image-C-4ysbGe.js → html-to-image-DfyxinVN.js} +2 -2
  36. package/dist/assets/{index-CiJmYQBT.js → index-D-wPCBTa.js} +7 -7
  37. package/dist/assets/{index-xs-zSSrX.css → index-D6Dp_PFj.css} +1 -1
  38. package/dist/assets/{layout-Dtql_o_T.js → layout-BWV4OadK.js} +5 -5
  39. package/dist/assets/logs-panel-DpMLUEOB.js +1 -0
  40. package/dist/assets/{markdown-renderer-dABDqZDw.js → markdown-renderer-DtzxgcfB.js} +2 -2
  41. package/dist/assets/{name-cell-input-Y4knmEIU.js → name-cell-input-SbknxDvi.js} +1 -1
  42. package/dist/assets/{outline-panel-CQ_KWLip.js → outline-panel-kV7ARoXE.js} +1 -1
  43. package/dist/assets/{packages-panel-Ijt7d51P.js → packages-panel-B3biBPQ1.js} +1 -1
  44. package/dist/assets/{panels-DfU1Oikj.js → panels-BsrSW0bC.js} +2 -2
  45. package/dist/assets/process-output-DCAOrKEU.js +1 -0
  46. package/dist/assets/{radio-group-22eWJ9fv.js → radio-group-BWAhJtjV.js} +1 -1
  47. package/dist/assets/readonly-code-display-D_rgEfAm.js +1 -0
  48. package/dist/assets/{readonly-python-code-n_TrLmsR.js → readonly-python-code-B5ABqdqK.js} +1 -1
  49. package/dist/assets/{reveal-component-CCLTV1q6.js → reveal-component-CrcdxGl_.js} +2 -2
  50. package/dist/assets/{run-page-DbezK7Qs.js → run-page-DFHpKr66.js} +1 -1
  51. package/dist/assets/{scratchpad-panel-C2wAEv5n.js → scratchpad-panel-CfqnZK3C.js} +1 -1
  52. package/dist/assets/{session-panel-BZGNcZmp.js → session-panel-DWS61Z1x.js} +1 -1
  53. package/dist/assets/{skeleton-BzlKcq21.js → skeleton-PJnQ2_IA.js} +1 -1
  54. package/dist/assets/{snippets-panel-DNaNtkR0.js → snippets-panel-D1eovB0O.js} +1 -1
  55. package/dist/assets/{state-jCdh8Dcx.js → state-BjouLTuw.js} +1 -1
  56. package/dist/assets/{state-DFPAfLw7.js → state-Bu-aMpIc.js} +3 -3
  57. package/dist/assets/{textarea-DuEtSOCl.js → textarea-DhPaQ0wx.js} +1 -1
  58. package/dist/assets/{tracing-ttUd3J7U.js → tracing-B2ux60rQ.js} +1 -1
  59. package/dist/assets/{tracing-panel-rVAa3En0.js → tracing-panel-CF8H5wRv.js} +2 -2
  60. package/dist/assets/{tree-actions-UCr1tMHw.js → tree-actions-CPSDN4eE.js} +1 -1
  61. package/dist/assets/useCellActionButton-k2OXllYI.js +1 -0
  62. package/dist/assets/{useDeleteCell-PrgejW8A.js → useDeleteCell-C3nmnuxK.js} +1 -1
  63. package/dist/assets/{useDependencyPanelTab-5jc-_-hy.js → useDependencyPanelTab-D14Lna3z.js} +1 -1
  64. package/dist/assets/{useNotebookActions-4vw_NiRs.js → useNotebookActions-CHM0Ardg.js} +3 -3
  65. package/dist/assets/useRunCells-C4qD32Ix.js +1 -0
  66. package/dist/assets/useSplitCell-BEebdldD.js +1 -0
  67. package/dist/index.html +23 -23
  68. package/package.json +2 -3
  69. package/src/components/editor/chrome/panels/__tests__/logs-panel.test.tsx +25 -0
  70. package/src/components/editor/chrome/panels/logs-panel.tsx +12 -2
  71. package/src/core/cells/__tests__/cells.test.ts +12 -2
  72. package/src/core/cells/__tests__/logs.test.ts +5 -5
  73. package/src/core/cells/cells.ts +13 -0
  74. package/src/core/cells/document-changes.ts +1 -0
  75. package/src/core/cells/logs.ts +14 -4
  76. package/src/core/codemirror/copilot/__tests__/language-server.test.ts +396 -100
  77. package/src/core/codemirror/copilot/__tests__/transport.test.ts +128 -540
  78. package/src/core/codemirror/copilot/language-server.ts +182 -70
  79. package/src/core/codemirror/copilot/transport.ts +15 -207
  80. package/src/core/codemirror/copilot/types.ts +9 -2
  81. package/src/core/codemirror/language/languages/python.ts +23 -11
  82. package/src/core/codemirror/lsp/__tests__/federated-lsp.test.ts +161 -0
  83. package/src/core/codemirror/lsp/__tests__/log-messages.test.ts +53 -0
  84. package/src/core/codemirror/lsp/__tests__/normalize-markdown-math.test.ts +25 -0
  85. package/src/core/codemirror/lsp/__tests__/notebook-lsp.test.ts +598 -156
  86. package/src/core/codemirror/lsp/__tests__/transports.test.ts +112 -0
  87. package/src/core/codemirror/lsp/federated-lsp.ts +76 -23
  88. package/src/core/codemirror/lsp/log-messages.ts +37 -0
  89. package/src/core/codemirror/lsp/normalize-markdown-math.ts +8 -4
  90. package/src/core/codemirror/lsp/notebook-lsp.ts +220 -56
  91. package/src/core/codemirror/lsp/transports.ts +8 -1
  92. package/src/core/codemirror/lsp/types.ts +68 -8
  93. package/src/core/codemirror/lsp/utils.ts +14 -0
  94. package/src/core/lsp/__tests__/transport.test.ts +509 -337
  95. package/src/core/lsp/transport.ts +304 -132
  96. package/src/css/app/codemirror.css +16 -2
  97. package/src/stories/log-viewer.stories.tsx +12 -6
  98. package/dist/assets/ai-model-dropdown-BpVBUZnz.js +0 -5
  99. package/dist/assets/cells-6yd8nOAu.js +0 -231
  100. package/dist/assets/logs-panel-5u1wUBn1.js +0 -1
  101. package/dist/assets/process-output-Dd11YI04.js +0 -1
  102. package/dist/assets/readonly-code-display-BjK-TEri.js +0 -1
  103. package/dist/assets/useCellActionButton-CJknR0XY.js +0 -1
  104. package/dist/assets/useRunCells-DHsd7HGL.js +0 -1
  105. package/dist/assets/useSplitCell-MZpER0Bh.js +0 -1
@@ -38,7 +38,11 @@ import { createLspMarkdownRenderer } from "../../lsp/markdown-renderer";
38
38
  import { NotebookLanguageServerClient } from "../../lsp/notebook-lsp";
39
39
  import { createTransport } from "../../lsp/transports";
40
40
  import { CellDocumentUri, type ILanguageServerClient } from "../../lsp/types";
41
- import { getLspRootUri, getLspWorkspaceFolders } from "../../lsp/utils";
41
+ import {
42
+ getLspRootUri,
43
+ getLspWorkspaceFolders,
44
+ isKnownCellDocumentUri,
45
+ } from "../../lsp/utils";
42
46
  import {
43
47
  clickablePlaceholderExtension,
44
48
  smartPlaceholderExtension,
@@ -338,6 +342,21 @@ export class PythonLanguageAdapter implements LanguageAdapter<{}> {
338
342
  clients.length === 1
339
343
  ? (clients[0] as NotebookLanguageServerClient)
340
344
  : new FederatedLanguageServerClient(clients);
345
+ const openExternalLocation = (result: { uri: string }) => {
346
+ Logger.debug("openExternalLocation", result);
347
+ // Neither the merged document nor a cell is a file on disk. The
348
+ // plugin handles same-cell locations itself; revealing a location in
349
+ // another cell isn't supported yet.
350
+ if (
351
+ client.documentUri === result.uri ||
352
+ isKnownCellDocumentUri(result.uri)
353
+ ) {
354
+ return;
355
+ }
356
+ getRequestClient().openFile({
357
+ path: result.uri.replace("file://", ""),
358
+ });
359
+ };
341
360
 
342
361
  return [
343
362
  languageServerWithClient({
@@ -346,6 +365,7 @@ export class PythonLanguageAdapter implements LanguageAdapter<{}> {
346
365
  allowHTMLContent: true,
347
366
  markdownRenderer: createLspMarkdownRenderer(),
348
367
  useSnippetOnCompletion: true,
368
+ clientSideFiltering: true,
349
369
  hoverConfig: hoverOptions,
350
370
  completionConfig: autocompleteOptions,
351
371
  // Default to false
@@ -362,16 +382,8 @@ export class PythonLanguageAdapter implements LanguageAdapter<{}> {
362
382
  rename: hotkeys.getHotkey("cell.renameSymbol").key,
363
383
  },
364
384
  completionMatchBefore,
365
- onGoToDefinition: (result) => {
366
- Logger.debug("onGoToDefinition", result);
367
- if (client.documentUri === result.uri) {
368
- // Local definition
369
- return;
370
- }
371
- getRequestClient().openFile({
372
- path: result.uri.replace("file://", ""),
373
- });
374
- },
385
+ onGoToDefinition: openExternalLocation,
386
+ onShowLocation: openExternalLocation,
375
387
  }),
376
388
  documentUri.of(CellDocumentUri.of(cellId)),
377
389
  ];
@@ -0,0 +1,161 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { describe, expect, it, type Mocked, vi } from "vitest";
4
+ import type * as LSP from "vscode-languageserver-protocol";
5
+ import { FederatedLanguageServerClient } from "../federated-lsp";
6
+ import type { ILanguageServerClient } from "../types";
7
+
8
+ function createClient(
9
+ supportedMethods: string[] = [],
10
+ ): Mocked<ILanguageServerClient> {
11
+ const client: Mocked<ILanguageServerClient> = {
12
+ ready: true,
13
+ capabilities: {},
14
+ initializePromise: Promise.resolve(),
15
+ clientCapabilities: {},
16
+ initialize: vi.fn(),
17
+ close: vi.fn(),
18
+ hasCapability: vi.fn((method) => supportedMethods.includes(method)),
19
+ textDocumentDidOpen: vi.fn().mockResolvedValue(false),
20
+ textDocumentDidChange: vi.fn(),
21
+ textDocumentDidClose: vi.fn(),
22
+ textDocumentWillSave: vi.fn(),
23
+ textDocumentWillSaveWaitUntil: vi.fn().mockResolvedValue(null),
24
+ textDocumentDidSave: vi.fn(),
25
+ textDocumentHover: vi.fn().mockResolvedValue({ contents: [] }),
26
+ textDocumentCompletion: vi.fn().mockResolvedValue(null),
27
+ completionItemResolve: vi.fn(),
28
+ textDocumentDefinition: vi.fn().mockResolvedValue(null),
29
+ textDocumentCodeAction: vi.fn().mockResolvedValue(null),
30
+ codeActionResolve: vi.fn(),
31
+ textDocumentRename: vi.fn().mockResolvedValue(null),
32
+ textDocumentPrepareRename: vi.fn().mockResolvedValue(null),
33
+ textDocumentSignatureHelp: vi.fn().mockResolvedValue(null),
34
+ onNotification: vi.fn().mockReturnValue(() => true),
35
+ };
36
+ return client;
37
+ }
38
+
39
+ describe("FederatedLanguageServerClient", () => {
40
+ it("routes requests using dynamic method capabilities", async () => {
41
+ const staticOnlyClient = createClient();
42
+ const dynamicClient = createClient(["textDocument/definition"]);
43
+ const definition: LSP.Location = {
44
+ uri: "file:///definition.py",
45
+ range: {
46
+ start: { line: 1, character: 2 },
47
+ end: { line: 1, character: 5 },
48
+ },
49
+ };
50
+ dynamicClient.textDocumentDefinition.mockResolvedValue(definition);
51
+ const client = new FederatedLanguageServerClient([
52
+ staticOnlyClient,
53
+ dynamicClient,
54
+ ]);
55
+ const params: LSP.DefinitionParams = {
56
+ textDocument: { uri: "file:///notebook.py" },
57
+ position: { line: 0, character: 0 },
58
+ };
59
+
60
+ await expect(client.textDocumentDefinition(params)).resolves.toEqual(
61
+ definition,
62
+ );
63
+ expect(client.hasCapability("textDocument/definition")).toBe(true);
64
+ expect(staticOnlyClient.textDocumentDefinition).not.toHaveBeenCalled();
65
+ expect(dynamicClient.textDocumentDefinition).toHaveBeenCalledWith(params);
66
+ });
67
+
68
+ it("prefers the first client's willSaveWaitUntil edits regardless of latency", async () => {
69
+ const slowEdits: LSP.TextEdit[] = [
70
+ {
71
+ range: {
72
+ start: { line: 0, character: 0 },
73
+ end: { line: 0, character: 1 },
74
+ },
75
+ newText: "first",
76
+ },
77
+ ];
78
+ const first = createClient();
79
+ const second = createClient();
80
+ // Answers last, but still wins
81
+ first.textDocumentWillSaveWaitUntil.mockImplementation(
82
+ () => new Promise((resolve) => setTimeout(() => resolve(slowEdits), 5)),
83
+ );
84
+ second.textDocumentWillSaveWaitUntil.mockResolvedValue([
85
+ {
86
+ range: {
87
+ start: { line: 1, character: 0 },
88
+ end: { line: 1, character: 1 },
89
+ },
90
+ newText: "second",
91
+ },
92
+ ]);
93
+ const client = new FederatedLanguageServerClient([first, second]);
94
+
95
+ await expect(
96
+ client.textDocumentWillSaveWaitUntil({
97
+ textDocument: { uri: "file:///notebook.py" },
98
+ reason: 1,
99
+ }),
100
+ ).resolves.toEqual(slowEdits);
101
+ expect(second.textDocumentWillSaveWaitUntil).toHaveBeenCalled();
102
+ });
103
+
104
+ it("skips clients that reject willSaveWaitUntil", async () => {
105
+ const failing = createClient();
106
+ const working = createClient();
107
+ const edits: LSP.TextEdit[] = [
108
+ {
109
+ range: {
110
+ start: { line: 0, character: 0 },
111
+ end: { line: 0, character: 1 },
112
+ },
113
+ newText: "ok",
114
+ },
115
+ ];
116
+ failing.textDocumentWillSaveWaitUntil.mockRejectedValue(
117
+ new Error("server exploded"),
118
+ );
119
+ working.textDocumentWillSaveWaitUntil.mockResolvedValue(edits);
120
+ const client = new FederatedLanguageServerClient([failing, working]);
121
+
122
+ await expect(
123
+ client.textDocumentWillSaveWaitUntil({
124
+ textDocument: { uri: "file:///notebook.py" },
125
+ reason: 1,
126
+ }),
127
+ ).resolves.toEqual(edits);
128
+ });
129
+
130
+ it("fans out document lifecycle and save notifications", async () => {
131
+ const first = createClient();
132
+ const second = createClient();
133
+ second.textDocumentDidOpen.mockResolvedValue(true);
134
+ const client = new FederatedLanguageServerClient([first, second]);
135
+ const openParams: LSP.DidOpenTextDocumentParams = {
136
+ textDocument: {
137
+ uri: "file:///notebook.py",
138
+ languageId: "python",
139
+ version: 1,
140
+ text: "value = 1",
141
+ },
142
+ };
143
+ const closeParams: LSP.DidCloseTextDocumentParams = {
144
+ textDocument: { uri: "file:///notebook.py" },
145
+ };
146
+ const saveParams: LSP.DidSaveTextDocumentParams = {
147
+ textDocument: { uri: "file:///notebook.py" },
148
+ text: "value = 1",
149
+ };
150
+
151
+ await expect(client.textDocumentDidOpen(openParams)).resolves.toBe(true);
152
+ await client.textDocumentDidClose(closeParams);
153
+ await client.textDocumentDidSave(saveParams);
154
+
155
+ for (const child of [first, second]) {
156
+ expect(child.textDocumentDidOpen).toHaveBeenCalledWith(openParams);
157
+ expect(child.textDocumentDidClose).toHaveBeenCalledWith(closeParams);
158
+ expect(child.textDocumentDidSave).toHaveBeenCalledWith(saveParams);
159
+ }
160
+ });
161
+ });
@@ -0,0 +1,53 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import type { JSONRPCMessage } from "@marimo-team/codemirror-languageserver";
4
+ import { beforeEach, describe, expect, it } from "vitest";
5
+ import { initialNotebookState, notebookAtom } from "@/core/cells/cells";
6
+ import { store } from "@/core/state/jotai";
7
+ import { handleLogMessage } from "../log-messages";
8
+
9
+ function logMessage(type: number, message: string): JSONRPCMessage {
10
+ return {
11
+ jsonrpc: "2.0",
12
+ method: "window/logMessage",
13
+ params: { type, message },
14
+ } as JSONRPCMessage;
15
+ }
16
+
17
+ describe("handleLogMessage", () => {
18
+ beforeEach(() => {
19
+ store.set(notebookAtom, initialNotebookState());
20
+ });
21
+
22
+ it("adds a log for each message, erroring on Error and Warning", () => {
23
+ handleLogMessage("pylsp", logMessage(1, "failed to start"));
24
+ handleLogMessage("pylsp", logMessage(2, "plugin not installed"));
25
+ handleLogMessage("pylsp", logMessage(3, "indexing"));
26
+
27
+ expect(store.get(notebookAtom).cellLogs).toMatchObject([
28
+ { level: "stderr", message: "failed to start" },
29
+ { level: "stderr", message: "plugin not installed" },
30
+ {
31
+ level: "stdout",
32
+ message: "indexing",
33
+ source: { type: "lsp", name: "pylsp" },
34
+ },
35
+ ]);
36
+ });
37
+
38
+ it("ignores messages that aren't logs", () => {
39
+ handleLogMessage("pylsp", { jsonrpc: "2.0", id: 1 } as JSONRPCMessage);
40
+ handleLogMessage("pylsp", {
41
+ jsonrpc: "2.0",
42
+ method: "textDocument/publishDiagnostics",
43
+ params: { uri: "file:///a", diagnostics: [] },
44
+ } as JSONRPCMessage);
45
+ handleLogMessage("pylsp", {
46
+ jsonrpc: "2.0",
47
+ method: "window/logMessage",
48
+ params: { type: 1 },
49
+ } as JSONRPCMessage);
50
+
51
+ expect(store.get(notebookAtom).cellLogs).toEqual([]);
52
+ });
53
+ });
@@ -90,6 +90,31 @@ Use \`:math:\` as text and :math:\`z\` as math.
90
90
  expect(normalized).not.toContain(":math:`z`");
91
91
  expect(normalized).toContain("||(z||)");
92
92
  });
93
+
94
+ it("leaves markdown-escaped brackets in type annotations alone", () => {
95
+ const markdown =
96
+ 'on\\_change (Optional\\[Callable\\]) and editable\\_columns (Union\\[list\\[str\\], Literal\\["all"\\]\\])';
97
+
98
+ expect(normalizeMarkdownMath(markdown)).toBe(markdown);
99
+ });
100
+
101
+ it("keeps the newlines around fenced code blocks", () => {
102
+ const markdown = `
103
+ \`\`\`python
104
+ class data_editor()
105
+ \`\`\`
106
+ ---
107
+ Inline \\[z^2\\] math forces normalization.
108
+
109
+ Done.
110
+ `.trim();
111
+
112
+ // A closing fence glued to the next line stops closing the block, and the
113
+ // rest of the document is swallowed into it.
114
+ expect(normalizeMarkdownMath(markdown)).toContain(
115
+ "class data_editor()\n```\n---\n",
116
+ );
117
+ });
93
118
  });
94
119
 
95
120
  describe("normalizeLspDocumentation", () => {