@marimo-team/islands 0.23.14-dev6 → 0.23.14-dev8

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 (33) hide show
  1. package/dist/{ConnectedDataExplorerComponent-Du3_nUzI.js → ConnectedDataExplorerComponent-DXBx_nQg.js} +4 -4
  2. package/dist/{chat-ui-BZxLHwyD.js → chat-ui-DYBcNEdd.js} +81 -81
  3. package/dist/{code-visibility-C90Am97q.js → code-visibility-DKavYkBl.js} +1030 -837
  4. package/dist/{formats-d6MhLuQ9.js → formats-WsOgyW_K.js} +1 -1
  5. package/dist/{glide-data-editor-DkzAInWG.js → glide-data-editor-qpmKyAPn.js} +2 -2
  6. package/dist/{html-to-image-CGp_08St.js → html-to-image-MqcD07Bw.js} +73 -72
  7. package/dist/{input-CbEz_aj_.js → input-BSdZp5Ng.js} +1 -1
  8. package/dist/main.js +254 -254
  9. package/dist/{mermaid-CJW9vIyO.js → mermaid-D-HYBMEV.js} +2 -2
  10. package/dist/{process-output-R6JsYrv3.js → process-output-Dt3icftd.js} +1 -1
  11. package/dist/{reveal-component-C_u9FY4_.js → reveal-component-CuFatyYX.js} +5 -5
  12. package/dist/{spec-Bv-XlYiv.js → spec-CnTgI25l.js} +1 -1
  13. package/dist/{toDate-D-l5s8nn.js → toDate-D1Z7ZXWh.js} +1 -1
  14. package/dist/{useAsyncData-1Dhzjfwf.js → useAsyncData-BMc8itk2.js} +1 -1
  15. package/dist/{useDeepCompareMemoize-CDWT3BDz.js → useDeepCompareMemoize-ZwmDBRDY.js} +1 -1
  16. package/dist/{useLifecycle-AHlswLw-.js → useLifecycle-CxffarYV.js} +1 -1
  17. package/dist/{useTheme-BrYvK-_A.js → useTheme-yGsGEk82.js} +26 -24
  18. package/dist/{vega-component-Pk6lyc_a.js → vega-component-BFJTyykA.js} +5 -5
  19. package/package.json +1 -1
  20. package/src/components/chat/acp/agent-panel.tsx +35 -1
  21. package/src/components/chat/chat-panel.tsx +68 -29
  22. package/src/components/editor/actions/useNotebookActions.tsx +2 -2
  23. package/src/components/editor/chrome/wrapper/__tests__/useOpenAiAssistant.test.ts +36 -0
  24. package/src/components/editor/chrome/wrapper/useAiPanel.ts +3 -1
  25. package/src/components/editor/chrome/wrapper/useOpenAiAssistant.ts +88 -0
  26. package/src/components/editor/errors/__tests__/auto-fix.test.ts +119 -0
  27. package/src/components/editor/errors/auto-fix.tsx +108 -34
  28. package/src/components/editor/errors/fix-mode.ts +1 -1
  29. package/src/components/editor/output/MarimoTracebackOutput.tsx +10 -1
  30. package/src/components/editor/output/__tests__/traceback.test.tsx +14 -6
  31. package/src/core/ai/config.ts +2 -2
  32. package/src/core/ai/state.ts +11 -0
  33. package/src/core/codemirror/utils.ts +15 -0
@@ -0,0 +1,119 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { beforeEach, describe, expect, it, vi } from "vitest";
4
+ import { cellId } from "@/__tests__/branded";
5
+ import type { MarimoError } from "@/core/kernel/messages";
6
+
7
+ const getDatasourceContext = vi.fn<(id: unknown) => string | null>(() => null);
8
+
9
+ vi.mock("@/core/ai/context/providers/datasource", () => ({
10
+ getDatasourceContext: (id: unknown) => getDatasourceContext(id),
11
+ }));
12
+
13
+ const { buildFixPrompt, buildFixPromptFromText } = await import("../auto-fix");
14
+
15
+ describe("buildFixPromptFromText", () => {
16
+ beforeEach(() => {
17
+ getDatasourceContext.mockReset();
18
+ getDatasourceContext.mockReturnValue(null);
19
+ });
20
+
21
+ it("includes the cell id in the header when provided", () => {
22
+ const prompt = buildFixPromptFromText("boom", cellId("cell-1"));
23
+ expect(prompt).toBe(
24
+ "My cell (id: cell-1) produced the following error. Please fix it:\n\nboom",
25
+ );
26
+ });
27
+
28
+ it("uses a generic header when no cell id is provided", () => {
29
+ const prompt = buildFixPromptFromText("boom");
30
+ expect(prompt).toBe(
31
+ "My code gives the following error. Please fix it:\n\nboom",
32
+ );
33
+ });
34
+
35
+ it("appends datasource context when explicitly requested for the cell", () => {
36
+ getDatasourceContext.mockReturnValue("@datasource://my_db");
37
+ const prompt = buildFixPromptFromText("boom", cellId("cell-1"), {
38
+ includeDatasourceContext: true,
39
+ });
40
+ expect(prompt).toBe(
41
+ "My cell (id: cell-1) produced the following error. Please fix it:\n\nboom\n\nDatabase schema: @datasource://my_db",
42
+ );
43
+ });
44
+
45
+ it("does not append datasource context by default", () => {
46
+ getDatasourceContext.mockReturnValue("@datasource://my_db");
47
+ const prompt = buildFixPromptFromText("boom", cellId("cell-1"));
48
+ expect(prompt).toBe(
49
+ "My cell (id: cell-1) produced the following error. Please fix it:\n\nboom",
50
+ );
51
+ expect(getDatasourceContext).not.toHaveBeenCalled();
52
+ });
53
+
54
+ it("does not look up datasource context without a cell id", () => {
55
+ buildFixPromptFromText("boom", undefined, {
56
+ includeDatasourceContext: true,
57
+ });
58
+ expect(getDatasourceContext).not.toHaveBeenCalled();
59
+ });
60
+ });
61
+
62
+ describe("buildFixPrompt", () => {
63
+ beforeEach(() => {
64
+ getDatasourceContext.mockReset();
65
+ getDatasourceContext.mockReturnValue(null);
66
+ });
67
+
68
+ it("uses the error message when present", () => {
69
+ const errors: MarimoError[] = [
70
+ { type: "sql-error", msg: "syntax error", sql_statement: "SELECT" },
71
+ ];
72
+ expect(buildFixPrompt(errors, cellId("cell-1"))).toBe(
73
+ "My cell (id: cell-1) produced the following error. Please fix it:\n\nsyntax error",
74
+ );
75
+ });
76
+
77
+ it("joins multiple errors with newlines", () => {
78
+ const errors: MarimoError[] = [
79
+ { type: "syntax", msg: "bad syntax" },
80
+ {
81
+ type: "exception",
82
+ exception_type: "ValueError",
83
+ msg: "bad value",
84
+ raising_cell: null,
85
+ },
86
+ ];
87
+ expect(buildFixPrompt(errors, cellId("cell-1"))).toBe(
88
+ "My cell (id: cell-1) produced the following error. Please fix it:\n\nbad syntax\nbad value",
89
+ );
90
+ });
91
+
92
+ it("falls back to the error type when there is no message", () => {
93
+ const errors: MarimoError[] = [
94
+ { type: "multiple-defs", name: "foo", cells: [cellId("foo")] },
95
+ ];
96
+ expect(buildFixPrompt(errors, cellId("cell-1"))).toBe(
97
+ "My cell (id: cell-1) produced the following error. Please fix it:\n\nmultiple-defs",
98
+ );
99
+ });
100
+
101
+ it("appends datasource context for SQL errors", () => {
102
+ getDatasourceContext.mockReturnValue("@datasource://my_db");
103
+ const errors: MarimoError[] = [
104
+ { type: "sql-error", msg: "syntax error", sql_statement: "SELECT" },
105
+ ];
106
+ expect(buildFixPrompt(errors, cellId("cell-1"))).toBe(
107
+ "My cell (id: cell-1) produced the following error. Please fix it:\n\nsyntax error\n\nDatabase schema: @datasource://my_db",
108
+ );
109
+ });
110
+
111
+ it("does not append datasource context for non-SQL errors", () => {
112
+ getDatasourceContext.mockReturnValue("@datasource://my_db");
113
+ const errors: MarimoError[] = [{ type: "syntax", msg: "bad syntax" }];
114
+ expect(buildFixPrompt(errors, cellId("cell-1"))).toBe(
115
+ "My cell (id: cell-1) produced the following error. Please fix it:\n\nbad syntax",
116
+ );
117
+ expect(getDatasourceContext).not.toHaveBeenCalled();
118
+ });
119
+ });
@@ -1,7 +1,14 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
3
  import { useAtomValue, useSetAtom, useStore } from "jotai";
4
- import { ChevronDownIcon, SparklesIcon, WrenchIcon } from "lucide-react";
4
+ import {
5
+ CheckIcon,
6
+ ChevronDownIcon,
7
+ HatGlasses,
8
+ type LucideIcon,
9
+ SparklesIcon,
10
+ WrenchIcon,
11
+ } from "lucide-react";
5
12
  import { Button } from "@/components/ui/button";
6
13
  import {
7
14
  DropdownMenu,
@@ -14,11 +21,46 @@ import { aiCompletionCellAtom } from "@/core/ai/state";
14
21
  import { notebookAtom, useCellActions } from "@/core/cells/cells";
15
22
  import type { CellId } from "@/core/cells/ids";
16
23
  import { aiFeaturesEnabledAtom } from "@/core/config/config";
24
+ import { getDatasourceContext } from "@/core/ai/context/providers/datasource";
17
25
  import { getAutoFixes } from "@/core/errors/errors";
18
26
  import type { MarimoError } from "@/core/kernel/messages";
19
27
  import { cn } from "@/utils/cn";
28
+ import { useOpenAiAssistant } from "../chrome/wrapper/useOpenAiAssistant";
20
29
  import { type FixMode, useFixMode } from "./fix-mode";
21
30
 
31
+ export function buildFixPromptFromText(
32
+ errorText: string,
33
+ cellId?: CellId,
34
+ {
35
+ includeDatasourceContext = false,
36
+ }: { includeDatasourceContext?: boolean } = {},
37
+ ): string {
38
+ const header =
39
+ cellId != null
40
+ ? `My cell (id: ${cellId}) produced the following error. Please fix it:`
41
+ : "My code gives the following error. Please fix it:";
42
+ let prompt = `${header}\n\n${errorText}`;
43
+ if (cellId != null && includeDatasourceContext) {
44
+ const datasourceContext = getDatasourceContext(cellId);
45
+ if (datasourceContext) {
46
+ prompt += `\n\nDatabase schema: ${datasourceContext}`;
47
+ }
48
+ }
49
+ return prompt;
50
+ }
51
+
52
+ export function buildFixPrompt(errors: MarimoError[], cellId: CellId): string {
53
+ const errorText = errors
54
+ .map((error) => ("msg" in error && error.msg ? error.msg : error.type))
55
+ .join("\n");
56
+ const includeDatasourceContext = errors.some(
57
+ (error) => error.type === "sql-error",
58
+ );
59
+ return buildFixPromptFromText(errorText, cellId, {
60
+ includeDatasourceContext,
61
+ });
62
+ }
63
+
22
64
  export const AutoFixButton = ({
23
65
  errors,
24
66
  cellId,
@@ -35,6 +77,7 @@ export const AutoFixButton = ({
35
77
  getAutoFixes(error, { aiEnabled: aiFeaturesEnabled }),
36
78
  );
37
79
  const setAiCompletionCell = useSetAtom(aiCompletionCellAtom);
80
+ const openAiAssistant = useOpenAiAssistant();
38
81
 
39
82
  if (autoFixes.length === 0) {
40
83
  return null;
@@ -67,6 +110,12 @@ export const AutoFixButton = ({
67
110
  editorView?.focus();
68
111
  };
69
112
 
113
+ const openAISidebar = () => {
114
+ openAiAssistant({
115
+ prompt: buildFixPrompt(errors, cellId),
116
+ });
117
+ };
118
+
70
119
  return (
71
120
  <div className={cn("my-2", className)}>
72
121
  {firstFix.fixType === "ai" ? (
@@ -74,6 +123,7 @@ export const AutoFixButton = ({
74
123
  tooltip={firstFix.description}
75
124
  openPrompt={() => handleFix(false)}
76
125
  applyAutofix={() => handleFix(true)}
126
+ openChat={openAISidebar}
77
127
  />
78
128
  ) : (
79
129
  <Tooltip content={firstFix.description} align="start">
@@ -92,23 +142,53 @@ export const AutoFixButton = ({
92
142
  );
93
143
  };
94
144
 
95
- const PromptIcon = SparklesIcon;
96
- const AutofixIcon = WrenchIcon;
145
+ interface FixModeConfig {
146
+ Icon: LucideIcon;
147
+ title: string;
148
+ description: string;
149
+ }
97
150
 
98
- const PromptTitle = "Suggest a prompt";
99
- const AutofixTitle = "Fix with AI";
151
+ const MODE_CONFIG: Record<FixMode, FixModeConfig> = {
152
+ autofix: {
153
+ Icon: WrenchIcon,
154
+ title: "Inline AI Fix",
155
+ description: "Apply AI fixes inline in the cell",
156
+ },
157
+ chat: {
158
+ Icon: HatGlasses,
159
+ title: "Fix with AI assistant",
160
+ description: "Open the AI sidebar to fix",
161
+ },
162
+ prompt: {
163
+ Icon: SparklesIcon,
164
+ title: "Suggest a prompt",
165
+ description: "Edit the prompt before applying",
166
+ },
167
+ };
168
+
169
+ const FIX_MODES: FixMode[] = ["autofix", "prompt", "chat"];
100
170
 
101
171
  export const AIFixButton = ({
102
172
  tooltip,
103
173
  openPrompt,
104
174
  applyAutofix,
175
+ openChat,
105
176
  }: {
106
177
  tooltip: string;
107
178
  openPrompt: () => void;
108
179
  applyAutofix: () => void;
180
+ openChat: () => void;
109
181
  }) => {
110
182
  const { fixMode, setFixMode } = useFixMode();
111
183
 
184
+ let onAction = openPrompt;
185
+ if (fixMode === "chat") {
186
+ onAction = openChat;
187
+ } else if (fixMode === "autofix") {
188
+ onAction = applyAutofix;
189
+ }
190
+ const { Icon, title } = MODE_CONFIG[fixMode];
191
+
112
192
  return (
113
193
  <div className="flex">
114
194
  <Tooltip content={tooltip} align="start">
@@ -116,14 +196,10 @@ export const AIFixButton = ({
116
196
  size="xs"
117
197
  variant="outline"
118
198
  className="font-normal rounded-r-none border-r-0"
119
- onClick={fixMode === "prompt" ? openPrompt : applyAutofix}
199
+ onClick={onAction}
120
200
  >
121
- {fixMode === "prompt" ? (
122
- <PromptIcon className="h-3 w-3 mr-2 mb-0.5" />
123
- ) : (
124
- <AutofixIcon className="h-3 w-3 mr-2 mb-0.5" />
125
- )}
126
- {fixMode === "prompt" ? PromptTitle : AutofixTitle}
201
+ <Icon className="h-3 w-3 mr-2 mb-0.5" />
202
+ {title}
127
203
  </Button>
128
204
  </Tooltip>
129
205
  <DropdownMenu>
@@ -138,40 +214,38 @@ export const AIFixButton = ({
138
214
  </Button>
139
215
  </DropdownMenuTrigger>
140
216
  <DropdownMenuContent align="end" className="w-56">
141
- <DropdownMenuItem
142
- className="flex items-center gap-2"
143
- onClick={() => {
144
- setFixMode(fixMode === "prompt" ? "autofix" : "prompt");
145
- }}
146
- >
147
- <AiModeItem mode={fixMode === "prompt" ? "autofix" : "prompt"} />
148
- </DropdownMenuItem>
217
+ {FIX_MODES.map((mode) => (
218
+ <DropdownMenuItem
219
+ key={mode}
220
+ className="flex items-center gap-2"
221
+ onClick={() => setFixMode(mode)}
222
+ >
223
+ <AiModeItem mode={mode} selected={mode === fixMode} />
224
+ </DropdownMenuItem>
225
+ ))}
149
226
  </DropdownMenuContent>
150
227
  </DropdownMenu>
151
228
  </div>
152
229
  );
153
230
  };
154
231
 
155
- const AiModeItem = ({ mode }: { mode: FixMode }) => {
156
- const icon =
157
- mode === "prompt" ? (
158
- <PromptIcon className="h-4 w-4" />
159
- ) : (
160
- <AutofixIcon className="h-4 w-4" />
161
- );
162
- const title = mode === "prompt" ? PromptTitle : AutofixTitle;
163
- const description =
164
- mode === "prompt"
165
- ? "Edit the prompt before applying"
166
- : "Apply AI fixes automatically";
232
+ const AiModeItem = ({
233
+ mode,
234
+ selected,
235
+ }: {
236
+ mode: FixMode;
237
+ selected: boolean;
238
+ }) => {
239
+ const { Icon, title, description } = MODE_CONFIG[mode];
167
240
 
168
241
  return (
169
- <div className="flex items-center gap-2">
170
- {icon}
242
+ <div className="flex items-center gap-2 w-full">
243
+ <Icon className="h-4 w-4 shrink-0" />
171
244
  <div className="flex flex-col">
172
245
  <span className="font-medium">{title}</span>
173
246
  <span className="text-xs text-muted-foreground">{description}</span>
174
247
  </div>
248
+ {selected && <CheckIcon className="h-4 w-4 ml-auto shrink-0" />}
175
249
  </div>
176
250
  );
177
251
  };
@@ -4,7 +4,7 @@ import { useAtom } from "jotai";
4
4
  import { atomWithStorage } from "jotai/utils";
5
5
  import { jotaiJsonStorage } from "@/utils/storage/jotai";
6
6
 
7
- export type FixMode = "prompt" | "autofix";
7
+ export type FixMode = "prompt" | "autofix" | "chat";
8
8
 
9
9
  const BASE_KEY = "marimo:ai-autofix-mode";
10
10
 
@@ -41,7 +41,8 @@ import {
41
41
  extractAllTracebackInfo,
42
42
  getTracebackInfo,
43
43
  } from "@/utils/traceback";
44
- import { AIFixButton } from "../errors/auto-fix";
44
+ import { useOpenAiAssistant } from "../chrome/wrapper/useOpenAiAssistant";
45
+ import { AIFixButton, buildFixPromptFromText } from "../errors/auto-fix";
45
46
  import { MangledSegments } from "../errors/mangled-local-chip";
46
47
  import { CellLinkTraceback } from "../links/cell-link";
47
48
  import type { OnRefactorWithAI } from "../Output";
@@ -71,6 +72,7 @@ export const MarimoTracebackOutput = ({
71
72
 
72
73
  const lastTracebackLine = lastLine(traceback);
73
74
  const aiFeaturesEnabled = useAtomValue(aiFeaturesEnabledAtom);
75
+ const openAiAssistant = useOpenAiAssistant();
74
76
 
75
77
  // Get last traceback info
76
78
  const tracebackInfo = extractAllTracebackInfo(traceback)?.at(0);
@@ -97,6 +99,12 @@ export const MarimoTracebackOutput = ({
97
99
  });
98
100
  };
99
101
 
102
+ const openAISidebar = () => {
103
+ openAiAssistant({
104
+ prompt: buildFixPromptFromText(lastTracebackLine, cellId),
105
+ });
106
+ };
107
+
100
108
  return (
101
109
  <div className="flex flex-col gap-2 min-w-full w-fit">
102
110
  <button
@@ -126,6 +134,7 @@ export const MarimoTracebackOutput = ({
126
134
  tooltip="Fix with AI"
127
135
  openPrompt={() => handleRefactorWithAI(false)}
128
136
  applyAutofix={() => handleRefactorWithAI(true)}
137
+ openChat={openAISidebar}
129
138
  />
130
139
  )}
131
140
  {showDebugger && (
@@ -1,11 +1,14 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
3
  import { render } from "@testing-library/react";
4
+ import { Provider } from "jotai";
4
5
  import { beforeAll, beforeEach, describe, expect, test, vi } from "vitest";
6
+ import { MockRequestClient } from "@/__mocks__/requests";
5
7
  import { Tracebacks } from "@/__mocks__/tracebacks";
6
8
  import { cellId } from "@/__tests__/branded";
7
9
  import { TooltipProvider } from "@/components/ui/tooltip";
8
10
  import { initialModeAtom } from "@/core/mode";
11
+ import { requestClientAtom } from "@/core/network/requests";
9
12
  import { store } from "@/core/state/jotai";
10
13
  import { renderHTML } from "@/plugins/core/RenderHTML";
11
14
  import {
@@ -20,13 +23,16 @@ describe("traceback component", () => {
20
23
  beforeEach(() => {
21
24
  vi.resetAllMocks();
22
25
  store.set(initialModeAtom, "edit");
26
+ store.set(requestClientAtom, MockRequestClient.create());
23
27
  });
24
28
 
25
29
  test("extracts cell-link", () => {
26
30
  const traceback = (
27
- <TooltipProvider>
28
- <MarimoTracebackOutput traceback={Tracebacks.raw} cellId={cid} />
29
- </TooltipProvider>
31
+ <Provider store={store}>
32
+ <TooltipProvider>
33
+ <MarimoTracebackOutput traceback={Tracebacks.raw} cellId={cid} />
34
+ </TooltipProvider>
35
+ </Provider>
30
36
  );
31
37
  const { unmount, getAllByRole } = render(traceback);
32
38
 
@@ -44,9 +50,11 @@ describe("traceback component", () => {
44
50
 
45
51
  test("renames File to Cell for relevant lines", () => {
46
52
  const traceback = (
47
- <TooltipProvider>
48
- <MarimoTracebackOutput traceback={Tracebacks.raw} cellId={cid} />
49
- </TooltipProvider>
53
+ <Provider store={store}>
54
+ <TooltipProvider>
55
+ <MarimoTracebackOutput traceback={Tracebacks.raw} cellId={cid} />
56
+ </TooltipProvider>
57
+ </Provider>
50
58
  );
51
59
  const { unmount, container } = render(traceback);
52
60
 
@@ -60,7 +60,7 @@ export const useModelChange = () => {
60
60
  },
61
61
  };
62
62
 
63
- saveConfig(newConfig);
63
+ await saveConfig(newConfig);
64
64
  };
65
65
 
66
66
  const saveModeChange = async (newMode: CopilotMode) => {
@@ -71,7 +71,7 @@ export const useModelChange = () => {
71
71
  },
72
72
  };
73
73
 
74
- saveConfig(newConfig);
74
+ await saveConfig(newConfig);
75
75
  };
76
76
 
77
77
  return { saveModelChange, saveModeChange };
@@ -21,6 +21,17 @@ export interface AiCompletionCell {
21
21
 
22
22
  export const aiCompletionCellAtom = atom<AiCompletionCell | null>(null);
23
23
 
24
+ /**
25
+ * A prompt queued to be delivered to the AI assistant panel.
26
+ * The active panel consumes this on mount/when ready, then clears it.
27
+ */
28
+ export interface PendingAiPrompt {
29
+ prompt: string;
30
+ submit: boolean;
31
+ }
32
+
33
+ export const pendingAiPromptAtom = atom<PendingAiPrompt | null>(null);
34
+
24
35
  const INCLUDE_OTHER_CELLS_KEY = "marimo:ai:includeOtherCells";
25
36
  export const includeOtherCellsAtom = atomWithStorage<boolean>(
26
37
  INCLUDE_OTHER_CELLS_KEY,
@@ -2,6 +2,7 @@
2
2
  import type { EditorState, Transaction } from "@codemirror/state";
3
3
  import type { EditorView, ViewUpdate } from "@codemirror/view";
4
4
  import { getCM } from "@replit/codemirror-vim";
5
+ import type { ReactCodeMirrorRef } from "@uiw/react-codemirror";
5
6
 
6
7
  export function isAtStartOfEditor(ev: { state: EditorState }) {
7
8
  const main = ev.state.selection.main;
@@ -37,6 +38,20 @@ export function moveToEndOfEditor(ev: EditorView | undefined) {
37
38
  });
38
39
  }
39
40
 
41
+ /** We delay the focus and move to end of the editor until React has rendered the prefilled value. */
42
+ export function focusInputAndMoveToEnd(
43
+ ref: React.RefObject<ReactCodeMirrorRef | null>,
44
+ ) {
45
+ requestAnimationFrame(() => {
46
+ const view = ref.current?.view;
47
+ if (!view) {
48
+ return;
49
+ }
50
+ view.focus();
51
+ moveToEndOfEditor(view);
52
+ });
53
+ }
54
+
40
55
  export function isInVimMode(ev: EditorView): boolean {
41
56
  return getCM(ev)?.state.vim != null;
42
57
  }