@marimo-team/islands 0.23.7-dev9 → 0.23.7-dev90

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 (153) hide show
  1. package/dist/{ConnectedDataExplorerComponent-DnRhpPMJ.js → ConnectedDataExplorerComponent-2lBNiUv6.js} +13 -13
  2. package/dist/{ErrorBoundary-Da4UeYxT.js → ErrorBoundary-D3wrPNma.js} +1 -1
  3. package/dist/{any-language-editor-DDubl8YH.js → any-language-editor-VWs_7v27.js} +5 -5
  4. package/dist/assets/__vite-browser-external-CAdMKBac.js +1 -0
  5. package/dist/assets/worker-CpBbwbQo.js +73 -0
  6. package/dist/{button-CA5pI2YF.js → button-Dj4BTre0.js} +5 -0
  7. package/dist/{capabilities-6laDasij.js → capabilities-C9rrYCzf.js} +1 -1
  8. package/dist/{chat-ui-BmWZZ3mE.js → chat-ui-D3XBept8.js} +625 -233
  9. package/dist/{check-CFM2mVDr.js → check-BcUIXnUT.js} +1 -1
  10. package/dist/{code-visibility-CRHzv49w.js → code-visibility-C5NrPsUC.js} +11480 -1992
  11. package/dist/{copy-TGGAUEWp.js → copy-DLf4aN7I.js} +2 -2
  12. package/dist/{dist-ESg7xyoD.js → dist-D3ZI9nhS.js} +2 -2
  13. package/dist/{error-banner-DnBPzEWg.js → error-banner-CVkfBUT3.js} +2 -2
  14. package/dist/{esm-Dd1z1auZ.js → esm-CWp0KQeK.js} +1 -1
  15. package/dist/{extends-CzJgxo2J.js → extends-vAi97cpa.js} +4 -4
  16. package/dist/{formats-CgaK7Gmx.js → formats-Dsy9kkZu.js} +3 -3
  17. package/dist/{glide-data-editor-B-3A3G02.js → glide-data-editor-DucgdjRo.js} +9 -9
  18. package/dist/{html-to-image-BwZL1Pkk.js → html-to-image-CpggM7u1.js} +2667 -2408
  19. package/dist/{input-BAOe64zx.js → input-D4kjoQUB.js} +8 -6
  20. package/dist/{label-BCWi-Oqu.js → label-BLqV33b1.js} +2 -2
  21. package/dist/{loader-BvW0-YWZ.js → loader-Dr8Qem8p.js} +1 -1
  22. package/dist/main.js +1697 -10282
  23. package/dist/{mermaid-cXSZ1pfD.js → mermaid-DO-Daq7u.js} +5 -5
  24. package/dist/{process-output-lpVrk7d5.js → process-output-X8TR20AK.js} +3 -3
  25. package/dist/reveal-component-kMIwe09M.js +7447 -0
  26. package/dist/{spec-DSIuqd3f.js → spec-hVaaZsY5.js} +4 -4
  27. package/dist/{strings-B_FOH6eV.js → strings-BiIhGaI8.js} +4 -4
  28. package/dist/style.css +1 -1
  29. package/dist/{swiper-component-BHs0PWwp.js → swiper-component-DlD2GU2g.js} +2 -2
  30. package/dist/{toDate-CHtl9vts.js → toDate-CIpC_34u.js} +33 -20
  31. package/dist/{tooltip-B0mtKTXm.js → tooltip-DRaMBu06.js} +3 -3
  32. package/dist/{types-DBtDeUKD.js → types-Dzuoc3LN.js} +1 -1
  33. package/dist/{useAsyncData-B6hCGywC.js → useAsyncData-C56Khv_R.js} +1 -1
  34. package/dist/{useDateFormatter-B3mCQMP3.js → useDateFormatter-B_9k85Ex.js} +2 -2
  35. package/dist/{useDeepCompareMemoize-CmwDuYUH.js → useDeepCompareMemoize-Dt98v2ua.js} +1 -1
  36. package/dist/{useIframeCapabilities-DbdLoEDm.js → useIframeCapabilities-BkYHTrss.js} +1 -1
  37. package/dist/{useLifecycle-CjMjllqy.js → useLifecycle-BF6-z62y.js} +3 -3
  38. package/dist/{useTheme-CByZUW0p.js → useTheme-DykuNHR2.js} +2 -2
  39. package/dist/{vega-component-C2BYPkfd.js → vega-component-cSdqoAxe.js} +10 -10
  40. package/dist/{zod-BxdsqRPd.js → zod-BWkcDORu.js} +1 -1
  41. package/package.json +3 -3
  42. package/src/components/chat/chat-components.tsx +47 -0
  43. package/src/components/chat/chat-display.tsx +41 -7
  44. package/src/components/chat/chat-panel.tsx +37 -10
  45. package/src/components/chat/chat-utils.ts +42 -20
  46. package/src/components/chat/reasoning-accordion.tsx +14 -3
  47. package/src/components/chat/tool-call/shared.ts +13 -0
  48. package/src/components/chat/tool-call/tool-approval-card.tsx +62 -0
  49. package/src/components/chat/tool-call/tool-args.tsx +26 -0
  50. package/src/components/chat/tool-call/tool-call-view.tsx +99 -0
  51. package/src/components/chat/tool-call/tool-error-card.tsx +81 -0
  52. package/src/components/chat/tool-call/tool-history-row.tsx +153 -0
  53. package/src/components/chat/tool-call/tool-result.tsx +101 -0
  54. package/src/components/data-table/__tests__/column-header.test.ts +3 -1
  55. package/src/components/data-table/__tests__/column-header.test.tsx +308 -0
  56. package/src/components/data-table/__tests__/filter-by-values-picker.test.tsx +112 -0
  57. package/src/components/data-table/__tests__/filter-pill-editor.test.tsx +261 -0
  58. package/src/components/data-table/__tests__/filters.test.ts +196 -49
  59. package/src/components/data-table/charts/components/form-fields.tsx +1 -0
  60. package/src/components/data-table/column-header.tsx +349 -170
  61. package/src/components/data-table/date-filter-inputs.tsx +325 -0
  62. package/src/components/data-table/filter-by-values-picker.tsx +70 -9
  63. package/src/components/data-table/filter-pill-editor.tsx +410 -156
  64. package/src/components/data-table/filter-pills.tsx +69 -54
  65. package/src/components/data-table/filters.ts +218 -101
  66. package/src/components/data-table/header-items.tsx +8 -1
  67. package/src/components/data-table/operator-labels.ts +25 -0
  68. package/src/components/data-table/regex-input.tsx +61 -0
  69. package/src/components/dependency-graph/minimap-content.tsx +14 -3
  70. package/src/components/editor/actions/pair-with-agent-modal.tsx +140 -49
  71. package/src/components/editor/actions/useNotebookActions.tsx +3 -1
  72. package/src/components/editor/app-container.tsx +7 -1
  73. package/src/components/editor/chrome/panels/context-aware-panel/context-aware-panel.tsx +10 -2
  74. package/src/components/editor/chrome/wrapper/app-chrome.tsx +1 -0
  75. package/src/components/editor/chrome/wrapper/footer-items/backend-status.tsx +1 -1
  76. package/src/components/editor/chrome/wrapper/footer.tsx +4 -1
  77. package/src/components/editor/chrome/wrapper/panels.tsx +4 -1
  78. package/src/components/editor/chrome/wrapper/sidebar.tsx +4 -1
  79. package/src/components/editor/controls/Controls.tsx +11 -3
  80. package/src/components/editor/file-tree/file-explorer.tsx +12 -2
  81. package/src/components/editor/header/__tests__/status.test.tsx +108 -0
  82. package/src/components/editor/header/status.tsx +44 -10
  83. package/src/components/editor/navigation/__tests__/clipboard.test.ts +106 -0
  84. package/src/components/editor/navigation/__tests__/navigation.test.ts +70 -0
  85. package/src/components/editor/navigation/clipboard.ts +99 -25
  86. package/src/components/editor/navigation/navigation.ts +15 -1
  87. package/src/components/editor/notebook-cell.tsx +5 -0
  88. package/src/components/editor/output/console/ConsoleOutput.tsx +23 -5
  89. package/src/components/editor/output/console/__tests__/ConsoleOutput.test.tsx +114 -0
  90. package/src/components/editor/renderers/slides-layout/__tests__/compute-slide-cells.test.ts +5 -4
  91. package/src/components/editor/renderers/slides-layout/__tests__/plugin.test.ts +55 -15
  92. package/src/components/editor/renderers/slides-layout/plugin.tsx +8 -25
  93. package/src/components/editor/renderers/slides-layout/slides-layout.tsx +19 -6
  94. package/src/components/editor/renderers/slides-layout/types.ts +40 -31
  95. package/src/components/editor/renderers/vertical-layout/vertical-layout.tsx +1 -0
  96. package/src/components/home/components.tsx +6 -0
  97. package/src/components/pages/run-page.tsx +4 -1
  98. package/src/components/scratchpad/scratchpad.tsx +1 -0
  99. package/src/components/slides/__tests__/slide-notes.test.ts +131 -0
  100. package/src/components/slides/reveal-component.tsx +252 -147
  101. package/src/components/slides/slide-notes-editor.tsx +127 -0
  102. package/src/components/slides/slide-notes.ts +64 -0
  103. package/src/components/slides/slides.css +14 -0
  104. package/src/components/ui/combobox.tsx +24 -5
  105. package/src/components/ui/number-field.tsx +2 -0
  106. package/src/core/ai/tools/__tests__/registry.test.ts +10 -12
  107. package/src/core/ai/tools/registry.ts +9 -5
  108. package/src/core/cells/__tests__/cells.test.ts +187 -0
  109. package/src/core/cells/__tests__/pending-cut-service.test.tsx +123 -0
  110. package/src/core/cells/cells.ts +102 -17
  111. package/src/core/cells/document-changes.ts +6 -1
  112. package/src/core/cells/pending-cut-service.ts +55 -0
  113. package/src/core/cells/utils.ts +11 -0
  114. package/src/core/codemirror/cells/extensions.ts +10 -0
  115. package/src/core/codemirror/go-to-definition/__tests__/commands.test.ts +152 -0
  116. package/src/core/codemirror/go-to-definition/__tests__/utils.test.ts +99 -0
  117. package/src/core/codemirror/go-to-definition/commands.ts +382 -22
  118. package/src/core/codemirror/go-to-definition/utils.ts +23 -5
  119. package/src/core/edit-app.tsx +3 -2
  120. package/src/core/hotkeys/hotkeys.ts +5 -0
  121. package/src/core/islands/worker/worker.tsx +3 -2
  122. package/src/core/run-app.tsx +2 -1
  123. package/src/core/runtime/__tests__/runtime.test.ts +38 -17
  124. package/src/core/runtime/runtime.ts +57 -34
  125. package/src/core/wasm/__tests__/utils.test.ts +34 -0
  126. package/src/core/wasm/utils.ts +14 -0
  127. package/src/core/wasm/worker/bootstrap.ts +3 -2
  128. package/src/core/wasm/worker/worker.ts +3 -2
  129. package/src/core/websocket/__tests__/useMarimoKernelConnection.hook.test.tsx +156 -0
  130. package/src/core/websocket/__tests__/useMarimoKernelConnection.test.ts +101 -0
  131. package/src/core/websocket/transports/__tests__/ws.test.ts +125 -0
  132. package/src/core/websocket/transports/basic.ts +1 -1
  133. package/src/core/websocket/transports/ws.ts +96 -0
  134. package/src/core/websocket/useMarimoKernelConnection.tsx +133 -54
  135. package/src/core/websocket/useWebSocket.tsx +3 -15
  136. package/src/css/app/Cell.css +10 -0
  137. package/src/plugins/core/__test__/sanitize.test.ts +30 -0
  138. package/src/plugins/impl/DropdownPlugin.tsx +12 -1
  139. package/src/plugins/impl/MultiselectPlugin.tsx +4 -0
  140. package/src/plugins/impl/SearchableSelect.tsx +11 -1
  141. package/src/plugins/impl/TabsPlugin.tsx +35 -7
  142. package/src/plugins/impl/__tests__/DropdownPlugin.test.tsx +56 -0
  143. package/src/plugins/impl/__tests__/TabsPlugin.test.tsx +154 -0
  144. package/src/plugins/impl/data-frames/forms/__tests__/__snapshots__/form.test.tsx.snap +48 -36
  145. package/src/plugins/impl/data-frames/schema.ts +4 -1
  146. package/src/plugins/layout/DownloadPlugin.tsx +9 -7
  147. package/src/utils/__tests__/id-tree.test.ts +71 -0
  148. package/src/utils/download.ts +4 -2
  149. package/src/utils/id-tree.tsx +89 -0
  150. package/dist/assets/__vite-browser-external-rrUYDKRl.js +0 -1
  151. package/dist/assets/worker-Bfy15ViQ.js +0 -73
  152. package/dist/reveal-component-C97Ceb7e.js +0 -4863
  153. package/src/components/chat/tool-call-accordion.tsx +0 -247
@@ -1,7 +1,31 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
3
  import { syntaxTree } from "@codemirror/language";
4
+ import type { EditorState } from "@codemirror/state";
4
5
  import { EditorView } from "@codemirror/view";
6
+ import type { SyntaxNode, Tree, TreeCursor } from "@lezer/common";
7
+
8
+ const SCOPE_CREATING_NODES = new Set([
9
+ "FunctionDefinition",
10
+ "LambdaExpression",
11
+ "ArrayComprehensionExpression",
12
+ "SetComprehensionExpression",
13
+ "DictionaryComprehensionExpression",
14
+ "ComprehensionExpression",
15
+ "ClassDefinition",
16
+ ]);
17
+
18
+ const POSITION_SENSITIVE_SCOPES = new Set(["ClassDefinition"]);
19
+
20
+ interface ScopeContext {
21
+ id: number;
22
+ type: string;
23
+ }
24
+
25
+ interface VariableDeclaration {
26
+ from: number;
27
+ scopeId: number;
28
+ }
5
29
 
6
30
  function goToPosition(view: EditorView, from: number): void {
7
31
  view.focus();
@@ -22,50 +46,386 @@ function goToPosition(view: EditorView, from: number): void {
22
46
  });
23
47
  }
24
48
 
25
- /**
26
- * This function will select the first occurrence of the given variable name,
27
- * for a given editor view.
28
- * @param view The editor view which contains the variable name.
29
- * @param variableName The name of the variable to select, if found in the editor.
30
- */
31
- export function goToVariableDefinition(
32
- view: EditorView,
49
+ function findFirstMatchingVariable(
50
+ state: EditorState,
33
51
  variableName: string,
34
- ): boolean {
35
- const { state } = view;
52
+ ): number | null {
36
53
  const tree = syntaxTree(state);
37
54
 
38
- let found = false;
39
- let from = 0;
55
+ let from: number | null = null;
40
56
 
41
57
  tree.iterate({
42
58
  enter: (node) => {
43
- if (found) {
59
+ if (from !== null) {
44
60
  return false;
45
- } // Stop traversal if found
61
+ }
46
62
 
47
- // Check if the node is an identifier and matches the variable name
48
63
  if (
49
64
  node.name === "VariableName" &&
50
65
  state.doc.sliceString(node.from, node.to) === variableName
51
66
  ) {
52
67
  from = node.from;
53
- found = true;
54
- return false; // Stop traversal
68
+ return false;
55
69
  }
56
70
 
57
- // Skip comments and strings
58
71
  if (node.name === "Comment" || node.name === "String") {
59
72
  return false;
60
73
  }
74
+
75
+ return undefined;
61
76
  },
62
77
  });
63
78
 
64
- if (found) {
65
- goToPosition(view, from);
66
- return true;
79
+ return from;
80
+ }
81
+
82
+ function getScopeChain(tree: Tree, usagePosition: number): ScopeContext[] {
83
+ const scopeChain: ScopeContext[] = [];
84
+ let currentNode: SyntaxNode | null = tree.resolveInner(usagePosition, 0);
85
+
86
+ while (currentNode) {
87
+ if (SCOPE_CREATING_NODES.has(currentNode.name)) {
88
+ // Skip ClassDefinition if we've already seen a function/lambda.
89
+ const inFunctionLikeScope = scopeChain.some(
90
+ (scope) =>
91
+ scope.type === "FunctionDefinition" ||
92
+ scope.type === "LambdaExpression",
93
+ );
94
+ if (!(inFunctionLikeScope && currentNode.name === "ClassDefinition")) {
95
+ scopeChain.push({
96
+ id: currentNode.from,
97
+ type: currentNode.name,
98
+ });
99
+ }
100
+ }
101
+ currentNode = currentNode.parent;
102
+ }
103
+
104
+ scopeChain.push({ id: -1, type: "global" });
105
+ return scopeChain;
106
+ }
107
+
108
+ function addDeclaration(
109
+ declarations: VariableDeclaration[],
110
+ scopeId: number,
111
+ from: number,
112
+ ) {
113
+ declarations.push({ scopeId, from });
114
+ }
115
+
116
+ function traverseChildren(
117
+ cursor: TreeCursor,
118
+ callback: (node: SyntaxNode) => void,
119
+ ) {
120
+ if (cursor.firstChild()) {
121
+ do {
122
+ callback(cursor.node);
123
+ } while (cursor.nextSibling());
124
+ }
125
+ }
126
+
127
+ function collectMatchingTargets(
128
+ cursor: TreeCursor,
129
+ state: EditorState,
130
+ variableName: string,
131
+ scopeId: number,
132
+ declarations: VariableDeclaration[],
133
+ ) {
134
+ switch (cursor.name) {
135
+ case "VariableName":
136
+ if (state.doc.sliceString(cursor.from, cursor.to) === variableName) {
137
+ addDeclaration(declarations, scopeId, cursor.from);
138
+ }
139
+ break;
140
+
141
+ case "TupleExpression":
142
+ case "ArrayExpression": {
143
+ const childCursor = cursor.node.cursor();
144
+ childCursor.firstChild();
145
+ do {
146
+ collectMatchingTargets(
147
+ childCursor,
148
+ state,
149
+ variableName,
150
+ scopeId,
151
+ declarations,
152
+ );
153
+ } while (childCursor.nextSibling());
154
+ break;
155
+ }
156
+ default:
157
+ break;
67
158
  }
68
- return false;
159
+ }
160
+
161
+ function collectFunctionParameters(
162
+ node: SyntaxNode | Tree,
163
+ state: EditorState,
164
+ variableName: string,
165
+ scopeId: number,
166
+ declarations: VariableDeclaration[],
167
+ ) {
168
+ const cursor = node.cursor();
169
+ cursor.firstChild();
170
+ do {
171
+ if (cursor.name !== "ParamList") {
172
+ continue;
173
+ }
174
+
175
+ const paramCursor = cursor.node.cursor();
176
+ paramCursor.firstChild();
177
+ do {
178
+ if (
179
+ paramCursor.name === "VariableName" &&
180
+ state.doc.sliceString(paramCursor.from, paramCursor.to) === variableName
181
+ ) {
182
+ addDeclaration(declarations, scopeId, paramCursor.from);
183
+ }
184
+ } while (paramCursor.nextSibling());
185
+ } while (cursor.nextSibling());
186
+ }
187
+
188
+ function collectForTargets(
189
+ node: SyntaxNode | Tree,
190
+ state: EditorState,
191
+ variableName: string,
192
+ scopeId: number,
193
+ declarations: VariableDeclaration[],
194
+ ) {
195
+ const cursor = node.cursor();
196
+ cursor.firstChild();
197
+ let foundFor = false;
198
+ do {
199
+ if (cursor.name === "for") {
200
+ foundFor = true;
201
+ } else if (foundFor && cursor.name === "in") {
202
+ break;
203
+ } else if (foundFor) {
204
+ collectMatchingTargets(
205
+ cursor,
206
+ state,
207
+ variableName,
208
+ scopeId,
209
+ declarations,
210
+ );
211
+ }
212
+ } while (cursor.nextSibling());
213
+ }
214
+
215
+ function collectMatchingDeclarations(
216
+ node: SyntaxNode | Tree,
217
+ state: EditorState,
218
+ variableName: string,
219
+ scopeStack: number[],
220
+ declarations: VariableDeclaration[],
221
+ ) {
222
+ const cursor = node.cursor();
223
+ const nodeName = cursor.name;
224
+ const nodeStart = cursor.from;
225
+
226
+ const isNewScope = SCOPE_CREATING_NODES.has(nodeName);
227
+ const currentScopeStack = isNewScope
228
+ ? [...scopeStack, nodeStart]
229
+ : scopeStack;
230
+ const currentScope = currentScopeStack[currentScopeStack.length - 1] ?? -1;
231
+
232
+ switch (nodeName) {
233
+ case "FunctionDefinition":
234
+ case "ClassDefinition": {
235
+ const subCursor = node.cursor();
236
+ subCursor.firstChild();
237
+ do {
238
+ if (
239
+ subCursor.name === "VariableName" &&
240
+ state.doc.sliceString(subCursor.from, subCursor.to) === variableName
241
+ ) {
242
+ const parentScope = scopeStack[scopeStack.length - 1] ?? -1;
243
+ addDeclaration(declarations, parentScope, subCursor.from);
244
+ break;
245
+ }
246
+ } while (subCursor.nextSibling());
247
+
248
+ if (nodeName === "FunctionDefinition") {
249
+ collectFunctionParameters(
250
+ node,
251
+ state,
252
+ variableName,
253
+ nodeStart,
254
+ declarations,
255
+ );
256
+ }
257
+ break;
258
+ }
259
+ case "LambdaExpression":
260
+ collectFunctionParameters(
261
+ node,
262
+ state,
263
+ variableName,
264
+ nodeStart,
265
+ declarations,
266
+ );
267
+ break;
268
+
269
+ case "ArrayComprehensionExpression":
270
+ case "DictionaryComprehensionExpression":
271
+ case "SetComprehensionExpression":
272
+ case "ComprehensionExpression":
273
+ case "ForStatement":
274
+ collectForTargets(node, state, variableName, currentScope, declarations);
275
+ break;
276
+
277
+ case "AssignStatement": {
278
+ const assignOpPositions: number[] = [];
279
+ const subCursor = node.cursor();
280
+ subCursor.firstChild();
281
+ do {
282
+ if (subCursor.name === "AssignOp") {
283
+ assignOpPositions.push(subCursor.from);
284
+ }
285
+ } while (subCursor.nextSibling());
286
+
287
+ const lastAssignOpPosition =
288
+ assignOpPositions[assignOpPositions.length - 1];
289
+ if (lastAssignOpPosition === undefined) {
290
+ break;
291
+ }
292
+
293
+ const targetCursor = node.cursor();
294
+ targetCursor.firstChild();
295
+ do {
296
+ if (targetCursor.from < lastAssignOpPosition) {
297
+ collectMatchingTargets(
298
+ targetCursor,
299
+ state,
300
+ variableName,
301
+ currentScope,
302
+ declarations,
303
+ );
304
+ }
305
+ } while (targetCursor.nextSibling());
306
+ break;
307
+ }
308
+ case "ImportStatement": {
309
+ const subCursor = node.cursor();
310
+ subCursor.firstChild();
311
+ do {
312
+ if (
313
+ subCursor.name === "VariableName" &&
314
+ state.doc.sliceString(subCursor.from, subCursor.to) === variableName
315
+ ) {
316
+ addDeclaration(declarations, currentScope, subCursor.from);
317
+ }
318
+ } while (subCursor.nextSibling());
319
+ break;
320
+ }
321
+ case "ImportFromStatement": {
322
+ const subCursor = node.cursor();
323
+ subCursor.firstChild();
324
+ let foundImport = false;
325
+ do {
326
+ if (subCursor.name === "import") {
327
+ foundImport = true;
328
+ } else if (
329
+ foundImport &&
330
+ subCursor.name === "VariableName" &&
331
+ state.doc.sliceString(subCursor.from, subCursor.to) === variableName
332
+ ) {
333
+ addDeclaration(declarations, currentScope, subCursor.from);
334
+ }
335
+ } while (subCursor.nextSibling());
336
+ break;
337
+ }
338
+ case "TryStatement":
339
+ case "WithStatement": {
340
+ const subCursor = node.cursor();
341
+ subCursor.firstChild();
342
+ let foundAs = false;
343
+ do {
344
+ if (subCursor.name === "as") {
345
+ foundAs = true;
346
+ } else if (
347
+ foundAs &&
348
+ subCursor.name === "VariableName" &&
349
+ state.doc.sliceString(subCursor.from, subCursor.to) === variableName
350
+ ) {
351
+ addDeclaration(declarations, currentScope, subCursor.from);
352
+ foundAs = false;
353
+ }
354
+ } while (subCursor.nextSibling());
355
+ break;
356
+ }
357
+ default:
358
+ break;
359
+ }
360
+
361
+ traverseChildren(cursor, (childNode) => {
362
+ collectMatchingDeclarations(
363
+ childNode,
364
+ state,
365
+ variableName,
366
+ currentScopeStack,
367
+ declarations,
368
+ );
369
+ });
370
+ }
371
+
372
+ function findScopedDefinitionPosition(
373
+ state: EditorState,
374
+ variableName: string,
375
+ usagePosition: number,
376
+ ): number | null {
377
+ const tree = syntaxTree(state);
378
+ const declarations: VariableDeclaration[] = [];
379
+
380
+ collectMatchingDeclarations(tree, state, variableName, [], declarations);
381
+
382
+ const clampedUsagePosition = Math.max(
383
+ 0,
384
+ Math.min(usagePosition, state.doc.length),
385
+ );
386
+
387
+ for (const scope of getScopeChain(tree, clampedUsagePosition)) {
388
+ const match = declarations
389
+ .filter((declaration) => declaration.scopeId === scope.id)
390
+ .filter((declaration) => {
391
+ return POSITION_SENSITIVE_SCOPES.has(scope.type)
392
+ ? declaration.from <= clampedUsagePosition
393
+ : true;
394
+ })
395
+ .toSorted((left, right) => left.from - right.from)[0];
396
+
397
+ if (match) {
398
+ return match.from;
399
+ }
400
+ }
401
+
402
+ return null;
403
+ }
404
+
405
+ /**
406
+ * This function will select the first occurrence of the given variable name,
407
+ * for a given editor view.
408
+ * @param view The editor view which contains the variable name.
409
+ * @param variableName The name of the variable to select, if found in the editor.
410
+ * @param usagePosition The position of the variable usage, if available.
411
+ */
412
+ export function goToVariableDefinition(
413
+ view: EditorView,
414
+ variableName: string,
415
+ usagePosition?: number,
416
+ ): boolean {
417
+ const { state } = view;
418
+ const from =
419
+ (usagePosition !== undefined
420
+ ? findScopedDefinitionPosition(state, variableName, usagePosition)
421
+ : null) ?? findFirstMatchingVariable(state, variableName);
422
+
423
+ if (from === null) {
424
+ return false;
425
+ }
426
+
427
+ goToPosition(view, from);
428
+ return true;
69
429
  }
70
430
 
71
431
  /**
@@ -18,10 +18,16 @@ function getWordUnderCursor(state: EditorState) {
18
18
  const { from, to } = state.selection.main;
19
19
  if (from === to) {
20
20
  const { startToken, endToken } = getPositionAtWordBounds(state.doc, from);
21
- return state.doc.sliceString(startToken, endToken);
21
+ return {
22
+ position: startToken,
23
+ word: state.doc.sliceString(startToken, endToken),
24
+ };
22
25
  }
23
26
 
24
- return state.doc.sliceString(from, to);
27
+ return {
28
+ position: from,
29
+ word: state.doc.sliceString(from, to),
30
+ };
25
31
  }
26
32
 
27
33
  /**
@@ -51,15 +57,15 @@ function isPrivateVariable(variableName: string) {
51
57
  */
52
58
  export function goToDefinitionAtCursorPosition(view: EditorView): boolean {
53
59
  const { state } = view;
54
- const variableName = getWordUnderCursor(state);
55
- if (!variableName) {
60
+ const { position, word } = getWordUnderCursor(state);
61
+ if (!word) {
56
62
  return false;
57
63
  }
58
64
  // Close popovers/tooltips
59
65
  closeCompletion(view);
60
66
  view.dispatch({ effects: closeHoverTooltips });
61
67
 
62
- return goToDefinition(view, variableName);
68
+ return goToDefinition(view, word, position);
63
69
  }
64
70
 
65
71
  /**
@@ -69,7 +75,19 @@ export function goToDefinitionAtCursorPosition(view: EditorView): boolean {
69
75
  export function goToDefinition(
70
76
  view: EditorView,
71
77
  variableName: string,
78
+ usagePosition?: number,
72
79
  ): boolean {
80
+ if (usagePosition !== undefined) {
81
+ const foundLocally = goToVariableDefinition(
82
+ view,
83
+ variableName,
84
+ usagePosition,
85
+ );
86
+ if (foundLocally) {
87
+ return true;
88
+ }
89
+ }
90
+
73
91
  // The variable may exist in another cell
74
92
  const editorWithVariable = getEditorForVariable(view, variableName);
75
93
  if (!editorWithVariable) {
@@ -79,7 +79,7 @@ export const EditApp: React.FC<AppProps> = ({
79
79
  };
80
80
  }, []);
81
81
 
82
- const { connection } = useMarimoKernelConnection({
82
+ const { connection, reconnect } = useMarimoKernelConnection({
83
83
  autoInstantiate: userConfig.runtime.auto_instantiate,
84
84
  setCells: (cells, layout) => {
85
85
  setCells(cells);
@@ -147,6 +147,7 @@ export const EditApp: React.FC<AppProps> = ({
147
147
  connection={connection}
148
148
  isRunning={isRunning}
149
149
  width={appConfig.width}
150
+ onReconnect={reconnect}
150
151
  >
151
152
  <AppHeader
152
153
  connection={connection}
@@ -156,7 +157,7 @@ export const EditApp: React.FC<AppProps> = ({
156
157
  "sticky left-0",
157
158
  )}
158
159
  >
159
- {isEditing && (
160
+ {!hideControls && isEditing && (
160
161
  <div className="flex items-center justify-center container">
161
162
  <FilenameForm filename={filename} />
162
163
  </div>
@@ -442,6 +442,11 @@ const DEFAULT_HOT_KEY = {
442
442
  group: "Command",
443
443
  key: "c",
444
444
  },
445
+ "command.cutCell": {
446
+ name: "Cut cell",
447
+ group: "Command",
448
+ key: "x",
449
+ },
445
450
  "command.pasteCell": {
446
451
  name: "Paste cell",
447
452
  group: "Command",
@@ -9,6 +9,7 @@ import {
9
9
  } from "rpc-anywhere";
10
10
  import type { NotificationPayload } from "@/core/kernel/messages";
11
11
  import type { ParentSchema } from "@/core/wasm/rpc";
12
+ import { shouldLoadDuckDBPackages } from "@/core/wasm/utils";
12
13
  import { TRANSPORT_ID } from "@/core/wasm/worker/constants";
13
14
  import { getPyodideVersion } from "@/core/wasm/worker/getPyodideVersion";
14
15
  import { MessageBuffer } from "@/core/wasm/worker/message-buffer";
@@ -85,8 +86,8 @@ const requestHandler = createRPCRequestHandler({
85
86
  loadPackages: async (code: string) => {
86
87
  await pyodideReadyPromise; // Make sure loading is done
87
88
 
88
- if (code.includes("mo.sql")) {
89
- // Add pandas and duckdb to the code
89
+ if (shouldLoadDuckDBPackages(code)) {
90
+ // Add pandas and duckdb to the code for mo.sql and for remote duckdb sources
90
91
  code = `import pandas\n${code}`;
91
92
  code = `import duckdb\n${code}`;
92
93
  code = `import sqlglot\n${code}`;
@@ -38,7 +38,7 @@ export const RunApp: React.FC<AppProps> = ({ appConfig }) => {
38
38
  };
39
39
  }, []);
40
40
 
41
- const { connection } = useMarimoKernelConnection({
41
+ const { connection, reconnect } = useMarimoKernelConnection({
42
42
  autoInstantiate: true,
43
43
  setCells: setCells,
44
44
  sessionId: getSessionId(),
@@ -84,6 +84,7 @@ export const RunApp: React.FC<AppProps> = ({ appConfig }) => {
84
84
  connection={connection}
85
85
  isRunning={isRunning}
86
86
  width={appConfig.width}
87
+ onReconnect={reconnect}
87
88
  >
88
89
  <AppHeader connection={connection} className="sm:pt-8">
89
90
  {galleryHref && (